wez Tue May 25 07:33:48 2004 EDT Added files: /livedocs livedoc_funcs.php pregenerate.php xml_classes5.php
Modified files: /livedocs configure.in livedoc.php mkindex.php /livedocs/themes/default html_format.php Log: Big commit; will be a little unclear what has changed. - Split the functions out from livedoc.php and into livedoc_funcs.php - Added PHP 5 version of xml_classes5.php; assignment using the reference operator leaks massive amounts of memory and will not be fixed. - Added pregenerate.php which should be run using PHP 5 (see above!) only to generate the entire manual as individual html pages. - Some minor corrections to Ilia's perf tweaks - Smarter entity handling Discovery: do_nav() is way too slow; don't attempt to pregenerate anything until it is tuned, unless you are a gentoo user with CPU to burn.
http://cvs.php.net/diff.php/livedocs/configure.in?r1=1.25&r2=1.26&ty=u Index: livedocs/configure.in diff -u livedocs/configure.in:1.25 livedocs/configure.in:1.26 --- livedocs/configure.in:1.25 Tue May 4 21:37:04 2004 +++ livedocs/configure.in Tue May 25 07:33:48 2004 @@ -1,5 +1,5 @@ ## A configure script -## $Id: configure.in,v 1.25 2004/05/05 01:37:04 wez Exp $ +## $Id: configure.in,v 1.26 2004/05/25 11:33:48 wez Exp $ AC_PREREQ(2.13) AC_INIT(livedoc.php) @@ -138,6 +138,7 @@ cp common.php config.php .htaccess $OUTPUTDIR rm -f .htaccess $lncmd $LIVEDOCS/livedoc.php $OUTPUTDIR/index.php + $lncmd $LIVEDOCS/livedoc_funcs.php $OUTPUTDIR/livedoc_funcs.php $lncmd $LIVEDOCS/error.php $OUTPUTDIR/error.php $lncmd $LIVEDOCS/style_mapping.php $OUTPUTDIR/style_mapping.php $lncmd $LIVEDOCS/xml_classes.php $OUTPUTDIR/xml_classes.php @@ -148,13 +149,12 @@ echo "" echo "You have configured livedocs so that links are relative to http://yourserver$WEBBASE" echo "$OUTPUTDIR is assumed to be the dir that is displayed by your webserver for that URL" -fi - -dnl Generate config.nice (primitive, but saves some brain power) -echo "#!/bin/sh" > config.nice -echo "./configure $*" >> config.nice -chmod +x config.nice + dnl Generate config.nice (primitive, but saves some brain power) + echo "#!/bin/sh" > config.nice + echo "./configure $*" >> config.nice + chmod +x config.nice +fi dnl vim:et:sw=2:ts=2 http://cvs.php.net/diff.php/livedocs/livedoc.php?r1=1.106&r2=1.107&ty=u Index: livedocs/livedoc.php diff -u livedocs/livedoc.php:1.106 livedocs/livedoc.php:1.107 --- livedocs/livedoc.php:1.106 Mon May 24 16:59:28 2004 +++ livedocs/livedoc.php Tue May 25 07:33:48 2004 @@ -18,15 +18,10 @@ // | Generate an HTML version of a phpdoc/docbook page on the fly | // +----------------------------------------------------------------------+ // -// $Id: livedoc.php,v 1.106 2004/05/24 20:59:28 iliaa Exp $ +// $Id: livedoc.php,v 1.107 2004/05/25 11:33:48 wez Exp $ define('LIVEDOC_SOURCE', dirname(__FILE__)); -include LIVEDOC_SOURCE . '/common.php'; -include LIVEDOC_SOURCE . '/xml_classes.php'; -include LIVEDOC_SOURCE . '/style_mapping.php'; -include LIVEDOC_SOURCE . '/handlers.php'; -include LIVEDOC_SOURCE . '/themes/' . THEME_NAME . '/html_format.php'; - +include LIVEDOC_SOURCE . '/livedoc_funcs.php'; $date = gmdate('Y-m-d H:i:s') . ' GMT'; if (isset($_GET['i'])) { @@ -56,27 +51,35 @@ * Aliases and XML -> style mapping * Please keep the arrays sorted by keys */ -$aliases = array( - // For Smarty - 'smarty.for.programmers' => 'smarty.constants', - 'smarty.for.designers' => 'language.basic.syntax', - 'api.functions' => 'handle_appendixes_funcref', - 'language.custom.functions' => 'handle_appendixes_funcref', - 'language.basic.syntax' => 'handle_appendixes_funcref', - 'language.variables' => 'handle_appendixes_funcref', - // For PHPdoc - 'api' => 'streams', - 'appendixes' => 'handle_appendixes_funcref', - 'faq' => 'faq.general', - 'features' => 'features.http-auth', - 'funcref' => 'handle_appendixes_funcref', - 'getting-started' => 'introduction', - 'installation' => 'install.general', - 'langref' => 'language.basic-syntax', - 'manual' => 'handle_contents', - 'security' => 'security.index', - 'indexes' => 'handle_index' -); + +if (BUILD_TYPE == 'smarty') { + $aliases = array( + // For Smarty + 'smarty.for.programmers' => 'smarty.constants', + 'smarty.for.designers' => 'language.basic.syntax', + 'api.functions' => 'handle_appendixes_funcref', + 'language.custom.functions' => 'handle_appendixes_funcref', + 'language.basic.syntax' => 'handle_appendixes_funcref', + 'language.variables' => 'handle_appendixes_funcref', + ); +} elseif (BUILD_TYPE == 'phpdoc') { + $aliases = array( + // For PHPdoc + 'api' => 'streams', + 'appendixes' => 'handle_appendixes_funcref', + 'faq' => 'faq.general', + 'features' => 'features.http-auth', + 'funcref' => 'handle_appendixes_funcref', + 'getting-started' => 'introduction', + 'installation' => 'install.general', + 'langref' => 'language.basic-syntax', + 'manual' => 'handle_contents', + 'security' => 'security.index', + 'indexes' => 'handle_index' + ); +} else { + $aliases = array(); +} /***************************************************************************** * Handle some special pages in a special way with special code @@ -158,393 +161,6 @@ fwrite($f, $contents); fclose($f); echo $contents; -} - -return; - -/***************************************************************************** - * Helper functions for navigation - */ -function do_nav($idx, $fb_idx, $lang, $current_page, &$nav, &$children) -{ - - $nav = "<table class='nav' border='0' cellpadding='0' cellspacing='0' width='150'>"; - - /* Get the fileinfo for the reference */ - $tr = sqlite_array_query($idx, "SELECT title, filename, idents.fileid, files.dirid from idents, files where id='$current_page' and idents.fileid=files.fileid", SQLITE_NUM); - if (!$tr) { - $tr = sqlite_array_query($fb_idx, "SELECT title, filename, idents.fileid, files.dirid from idents, files where id='$current_page' and idents.fileid=files.fileid", SQLITE_NUM); - } - list($title, $filename, $fileid, $dirid) = $tr; - - /* Get parent ID and child IDs */ - /* - first we get the first three parts of the path */ - if (($r = sqlite_single_query($idx, "SELECT path FROM toc WHERE docbook_id = '$current_page' LIMIT 1", SQLITE_NUM))) { - $path = explode(",", $r); - foreach ($path as $item) { - $nav .= do_nav_line($item, 'up', $current_page, $lang, $dummy); - } - $last_item = $item; - } else { - $nav .= do_nav_line('manual', 'up', $current_page, $lang, $dummy); - $last_item = 'manual'; - } - - /* With $last_item we're going to show all brothers */ - $r = (array) sqlite_single_query($idx, "SELECT docbook_id FROM toc WHERE parent_docbook_id = '$last_item' ORDER BY id"); - foreach ($r as $val) { - $nav .= do_nav_line($val, 'down', $current_page, $lang, $dummy); - } - - $children = array(); - /* And finally all children, but only if $current_page != $last_item - * because showing the same thing twice makes no sense */ - $r = (array) sqlite_single_query($idx, "SELECT docbook_id FROM toc WHERE parent_docbook_id = '$current_page' AND parent_docbook_id <> 'manual' ORDER BY id"); - foreach ($r as $val) { - $nav .= do_nav_line($val, 'downdown', $current_page, $lang, $title); - $children[$val] = $title; - } - - $nav .= "</table>\n"; - return $tr; -} - - -function generate_url_for_id($lang, $ref) -{ - static $cache = array(); - - if (isset($cache[$lang][$ref])) { - return $cache[$lang][$ref]; - } - - /* first determine the file in which the node can be found */ - $fileid = null; - $firstid = $ref; - - $saferef = sqlite_escape_string($ref); - - if (($fileid = sqlite_single_query($GLOBALS['idx'], "SELECT fileid from idents where id='$saferef'"))) { - $idx = $GLOBALS['idx']; - } else if (isset($GLOBALS['fb_idx']) && ($fileid = sqlite_single_query($GLOBALS['fb_idx'], "SELECT fileid from idents where id='$saferef'"))) { - $idx = $GLOBALS['fb_idx']; - } - - if (!$fileid) { /* determine the first node within that file */ - $firstid = sqlite_single_query($idx, "SELECT id from idents where fileid='$fileid' LIMIT 1"); - } - - if ($firstid != $ref) { - $hash = "#$ref"; - } else { - $hash = ""; - } - - if ($firstid == 'manual') { - $firstid = $ref; - $hash = ''; - } - - if (FORCE_DYNAMIC) { - $url = "{$_SERVER['PHP_SELF']}?l=$lang&q=$firstid$hash"; - } else { - $url = WEBBASE . "$lang/$firstid.html$hash"; - } - - $cache[$lang][$ref] = $url; - return $url; -} - -function do_nav_line($item, $class, $current_page, $lang, &$fulltitle) { - global $current_page_title; - - $nolink = FALSE; - - $title = lookup_title($item); - $fulltitle = $title; - if (strlen($title) > 25) { - $ftitle = " title='$title'"; - $title = substr($title, 0, 22). '...'; - } else { - $ftitle = ""; - if (!$title) { - $title = $item . ' [?]'; - $nolink = TRUE; - } - } - - if ($item == $current_page) { - $current_page_title = $title; - $title = "<b>$title</b>"; - } - $title = str_replace(' ', ' ', $title); - - if ($nolink) { - return "<tr><td class='$class'>$title</td></tr>\n"; - } else { - $url = generate_url_for_id($lang, $item); - return "<tr><td class='$class'><a $ftitle class='$class' href='$url'>$title</a></td></tr>\n"; - } -} - -/***************************************************************************** - * Search & Replace entities - */ -function bind_entities($data) { - - global $idx; - $sanity = 0; - - while (($ent_count = preg_match_all('/&([a-zA-Z0-9.-]+);/sm', $data, $matches)) && $sanity++ < 5) { - /* now collect their values */ - $ents = implode("','", array_unique($matches[1])); - $r = sqlite_array_query($idx, "SELECT entid, value from ents where entid in ('" . $ents . "')", SQLITE_NUM); - if ($r) { - $src = $dst = array(); - foreach ($r as $v) { - $src[] = '&' . $v[0] . ';'; - $dst[] = $v[1]; - } - /* substitute */ - $data = str_replace($src, $dst, $data); - } else { - /* no matching entities, no point in looping further */ - break; - } - } - - return $data; -} - - -/***************************************************************************** - * Helper functions for transformation - */ -function &load_xml_doc($filename, $included = false, $fallback_filename = '', $return_rev = 0) -{ - global $file_revision, $idx, $lang; - - $replace = array(); - $search = array('æ', '©', 'é', 'è', 'à', 'ï', 'ö', 'ä', 'Ä', - 'ô', 'ê', 'û', 'î', 'â', 'ë', 'ç', 'ù'); - foreach ($search as $item) { - $replace[] = html_entity_decode($item); - } - - $lang_rev = 0; - - $data = @file_get_contents($filename); - if (strlen($data) == 0 && strlen($fallback_filename)) { - $data = @file_get_contents($fallback_filename); - - if (strlen($data) == 0) { - $data = "<warning>permissions problem for $filename?</warning>"; - } - - } elseif ($lang != 'en' && preg_match('/<!-- EN-Revision: \d+\.(\d+)/', $data, $matches)) { - $lang_rev = $matches[1]; - } - - /* get file revision */ - if (empty($file_revision) && preg_match('/\$' . 'Revision: [^$]+ \$/', $data, $matches)) { - $file_revision .= $matches[0]; - } - - /* strip comments */ - $data = preg_replace('@<!--\s+.*\s-->@Usm', '', $data); - - /* Replace entities */ - $data = bind_entities($data); - - /* catch any undefined entities */ - if ($included) { - if (basename($filename) == 'functions.xml') { - $data = preg_replace('/&([a-zA-Z0-9-]+)\.([a-zA-Z0-9.-]+);/sme', 'make_function_link("\\1.\\2");', $data); - } else { - $data = preg_replace('/&([a-zA-Z0-9-]+)\.([a-zA-Z0-9.-]+);/sme', 'make_xref("\\1.\\2");', $data); - } - if (!$data || $data{1} != '?') { - $data = '<div>' . $data . '</div>'; - } else { - $data = preg_replace('@(<\\?xml.*\\?>)@U', '\\1<div>', $data) . '</div>'; - } - } else { - $data = preg_replace('/&([a-zA-Z0-9-]+)\.([a-zA-Z0-9.-]+);/sm', '<phpdoc_include ref="\\1.\\2"/>', $data); - } - - $data = str_replace($search, $replace, $data); - - $page = new DocBookToHTML($data); - - - if ($return_rev) { - return array($page, $lang_rev); - } else { - return $page; - } -} - -function format_user_notes($id) -{ - $notes = sqlite_array_query($GLOBALS['NOTESDB'], "SELECT id, xwhen, who, note from notes where sect='$id' order by xwhen desc"); - if (count($notes) == 0) - return ''; - - $inner = <<<HTML -<div class="usernotes"> - <span class="title">User Contributed Notes</span> -HTML; - - foreach ($notes as $note) { - $date = date("d-M-Y h:i", $note['xwhen']); - - $node = new stdClass; - $node->content = $note['note']; - $node->attributes['role'] = 'php'; - $the_note = format_listing($node); - - $inner .= <<<HTML -<div class="usernote"> - <div class="noteheader"> - <span class="user">$note[who]</span><br /> - <span class="when">$date</span> - </div> - $the_note -</div> -HTML; - } - - return $inner . "</div>"; -} - -function lookup_title($nodeid) -{ - static $ids = array(); - - /* Define your own title mappings here: */ - static $special = array ('indexes' => 'FunctionIndex'); - - /* we're going to do some tricky stuff here for languages, - * by using entities as much as possible */ - if (isset($special[$nodeid])) { - /* Check for the entity value */ - if (($r = sqlite_single_query($GLOBALS['idx'], "SELECT entid FROM ents WHERE entid = '$special[$nodeid]'"))) { - return $r; - } else { /* It's not an entity, let's just return it then */ - return $special[$nodeid]; - } - } - - /* See if the title lookup is in the cache already */ - if (isset($ids[$nodeid])) { - return $ids[$nodeid]; - } - - /* It's not, so we try to resolve it from the DB */ - $safeid = sqlite_escape_string($nodeid); - if (($ids[$nodeid] = sqlite_single_query($GLOBALS['idx'], "SELECT title from idents where id='$safeid'"))) { - return $ids[$nodeid]; - } else if (isset($GLOBALS['fb_idx']) && ($ids[$nodeid] = sqlite_single_query($GLOBALS['fb_idx'], "SELECT title from idents where id='$safeid'"))) { - return $ids[$nodeid]; - } - return null; -} - -/* transform an entity name of the form "reference.XXXX.functions.ID" - * and generate a link to its node using its title */ -function make_function_link($ref) -{ - $parts = explode('.', $ref); - - $stag = 'function'; - $etag = 'function'; - - switch (count($parts)) { - case 4: - $id = 'function.' . $parts[3]; - $func_name = lookup_title($id); - break; - - case 5: - if ($parts[3] == 'class') { - $id = 'class.' . $parts[4]; - $func_name = str_replace('-', '_', $parts[4]); - $stag = "xref linkend=\"$id\""; - $etag = "xref"; - break; - } - /* fall through */ - - default: - /* some weird node type we don't understand */ - $id = $ref; - $func_name = 'Unknown ??'; - } - - if ($parts[3] == 'class') { - $title = ' - ' . lookup_title($id) . ' class'; - } else { - $title = ' - ' . bind_entities(sqlite_single_query($GLOBALS['idx'], "SELECT descr from toc where docbook_id='$id'")); - } - - return "<div class=\"function_link\"><$stag>$func_name</$etag>$title</div>"; -} - -function make_xref($ref) -{ - $parts = explode('.', $ref); - if ($parts[0] == 'reference' && $parts[2] == 'functions') { - $id = 'function.'. $parts[3]; - } - $title = lookup_title($id); - if (!$title) { - $title = $id. ' [?]'; - } - return "<xref linkend=\"$id\">$title</xref>"; -} - -function do_contents($id, $level) -{ - global $lang; - - if (($r = sqlite_array_query($GLOBALS['idx'], "SELECT id, docbook_id FROM toc WHERE parent_id = ".$id, SQLITE_NUM))) { - foreach($r as $row) { - $ret .= "<li>"; - - if (!($title = lookup_title($row[1]))) { - $ret .= $row[1]. ' [?]'; - } else { - $ret .= "<a href='".generate_url_for_id($lang, $row[1])."'>".$title."</a>"; - } - - if (($level < 2 && strcmp($row[1], 'ref')) || $level < 1) { - $ret .= do_contents($row[0], $level + 1); - } - $ret .= "</li>\n"; - } - - return "<ul>" . $ret . "</ul>\n"; - } - - return ''; -} - -function handle_include($node) -{ - $filename = BASE . strtr($node->attributes['ref'], '.', '/') . '.xml'; - $fallback_filename = FALLBACK_BASE . strtr($node->attributes['ref'], '.', '/') . '.xml'; - - $doc = load_xml_doc($filename, true, $fallback_filename); - return $doc->transform($GLOBALS['map']); -} - -// Handle the case where a requested node id was not found -function handle_missing_index() -{ - global $current_page, $lang; - - header('Location: ' . WEBBASE . 'search.php?q=' . urlencode($current_page) . '&l=' . $lang); - exit; } ?> http://cvs.php.net/diff.php/livedocs/mkindex.php?r1=1.34&r2=1.35&ty=u Index: livedocs/mkindex.php diff -u livedocs/mkindex.php:1.34 livedocs/mkindex.php:1.35 --- livedocs/mkindex.php:1.34 Mon May 24 08:18:48 2004 +++ livedocs/mkindex.php Tue May 25 07:33:48 2004 @@ -19,7 +19,7 @@ // | construct an index | // +----------------------------------------------------------------------+ // -// $Id: mkindex.php,v 1.34 2004/05/24 12:18:48 wez Exp $ +// $Id: mkindex.php,v 1.35 2004/05/25 11:33:48 wez Exp $ /* just to be on the safe side */ @@ -212,7 +212,7 @@ if (!isset($replacements[$matches[1][$i]])) { $id = sqlite_escape_string($matches[1][$i]); $value = sqlite_escape_string($matches[2][$i]); - $strsql = "INSERT INTO ents VALUES ('$id', '$value')"; + $strsql = "INSERT INTO ents VALUES ('$id', '$value', 0)"; @sqlite_query($idx, $strsql); // echo $strsql . "\n"; // for debuging @@ -220,6 +220,34 @@ } } } + + $x = preg_match_all('/<!ENTITY\s+([a-zA-Z0-9.-]+)\s+SYSTEM\s+\'([^\']+)\'>/Usm', $d, $matches); + $docs = $GLOBALS['DOCS'] . DIRECTORY_SEPARATOR; + $docslen = strlen($docs); + $realdocs = realpath($docs) . DIRECTORY_SEPARATOR; + $reallen = strlen($realdocs); + $lang = $GLOBALS['LANG'] . DIRECTORY_SEPARATOR; + $langlen = strlen($lang); + for ($i = 0; $i < $x; $i++) { + $id = sqlite_escape_string($matches[1][$i]); + $value = sqlite_escape_string($matches[2][$i]); + echo "value: $value\ndocs: $docs\nreal: $realdocs\nlang: $lang\n"; + if (strncmp($value, $docs, $docslen) == 0) { + $value = substr($value, $docslen); + $check_lang = true; + } else if (strncmp($value, $realdocs, $reallen) == 0) { + $value = substr($value, $reallen); + $check_lang = true; + } else { + $check_lang = true; + } + + if ($check_lang && strncmp($value, $lang, $langlen) == 0) { + $value = substr($value, $langlen); + } + echo " ---> $value\n"; + @sqlite_query($idx, "INSERT INTO ents VALUES ('$id', '$value', 1)"); + } } return $replacements; } @@ -306,7 +334,8 @@ CREATE TABLE ents ( entid TEXT PRIMARY KEY, - value TEXT + value TEXT, + is_file INTEGER default 0 ); CREATE TABLE funcs ( @@ -324,6 +353,9 @@ title TEXT, descr TEXT ); +CREATE INDEX toc_parent_idx on toc(parent_id); +CREATE INDEX toc_docbook_id_idx on toc(docbook_id); +CREATE INDEX toc_parent_docbook_id_idx on toc(parent_docbook_id); CREATE TABLE searchi ( skey VARCHAR(230), http://cvs.php.net/diff.php/livedocs/themes/default/html_format.php?r1=1.10&r2=1.11&ty=u Index: livedocs/themes/default/html_format.php diff -u livedocs/themes/default/html_format.php:1.10 livedocs/themes/default/html_format.php:1.11 --- livedocs/themes/default/html_format.php:1.10 Mon May 24 08:43:25 2004 +++ livedocs/themes/default/html_format.php Tue May 25 07:33:48 2004 @@ -18,7 +18,7 @@ // | headers and footers for the HTML rendering | // +----------------------------------------------------------------------+ // -// $Id: html_format.php,v 1.10 2004/05/24 12:43:25 wez Exp $ +// $Id: html_format.php,v 1.11 2004/05/25 11:33:48 wez Exp $ // in livedoc.php @@ -32,7 +32,7 @@ "http://www.w3.org/TR/html4/loose.dtd"> <html lang="$lang"> <head> -<meta http-equiv="Content-Type" content="text/html; charset="$charset"/> +<meta http-equiv="Content-Type" content="text/html; charset=$charset"/> <title>$title</title> <link rel="stylesheet" href="$css_url" /> </head> http://cvs.php.net/co.php/livedocs/livedoc_funcs.php?r=1.1&p=1 Index: livedocs/livedoc_funcs.php +++ livedocs/livedoc_funcs.php <?php include LIVEDOC_SOURCE . '/common.php'; if (version_compare(phpversion(), "5", "ge")) { include LIVEDOC_SOURCE . '/xml_classes5.php'; } else { include LIVEDOC_SOURCE . '/xml_classes.php'; } include LIVEDOC_SOURCE . '/style_mapping.php'; include LIVEDOC_SOURCE . '/handlers.php'; include LIVEDOC_SOURCE . '/themes/' . THEME_NAME . '/html_format.php'; /***************************************************************************** * Helper functions for navigation */ function do_nav($idx, $fb_idx, $lang, $current_page, &$nav, &$children) { $children = array(); $nav = "<table class='nav' border='0' cellpadding='0' cellspacing='0' width='150'>"; /* Get the fileinfo for the reference */ list($tr) = sqlite_array_query($idx, "SELECT title, filename, idents.fileid, files.dirid from idents left join files where id='$current_page' and idents.fileid=files.fileid", SQLITE_NUM); if (!$tr) { list($tr) = sqlite_array_query($fb_idx, "SELECT title, filename, idents.fileid, files.dirid from idents left join files where id='$current_page' and idents.fileid=files.fileid", SQLITE_NUM); } if ($tr) { list($title, $filename, $fileid, $dirid) = $tr; } /* Get parent ID and child IDs */ /* - first we get the first three parts of the path */ $last_item = 'manual'; if (($r = sqlite_single_query($idx, "SELECT path FROM toc WHERE docbook_id = '$current_page' LIMIT 1", SQLITE_NUM))) { $path = explode(",", $r); foreach ($path as $item) { $nav .= do_nav_line($item, 'up', $current_page, $lang, $dummy); $last_item = $item; } } else { $nav .= do_nav_line('manual', 'up', $current_page, $lang, $dummy); } /* With $last_item we're going to show all brothers */ $r = (array) sqlite_single_query($idx, "SELECT docbook_id FROM toc WHERE parent_docbook_id = '$last_item' ORDER BY id"); foreach ($r as $val) { $nav .= do_nav_line($val, 'down', $current_page, $lang, $dummy); } /* And finally all children, but only if $current_page != $last_item * because showing the same thing twice makes no sense */ if ($current_page != $last_item && $current_page != 'manual') { $r = (array) sqlite_single_query($idx, "SELECT docbook_id FROM toc WHERE parent_docbook_id = '$current_page' ORDER BY id"); foreach ($r as $val) { $nav .= do_nav_line($val, 'downdown', $current_page, $lang, $title); $children[$val] = $title; } } $nav .= "</table>\n"; return $tr; } function generate_url_for_id($lang, $ref) { static $cache = array(); if (isset($cache[$lang][$ref])) { return $cache[$lang][$ref]; } /* first determine the file in which the node can be found */ $fileid = null; $firstid = $ref; $saferef = sqlite_escape_string($ref); $fileid = sqlite_single_query($GLOBALS['idx'], "SELECT fileid from idents where id='$saferef'"); if ($fileid) { $index = $GLOBALS['idx']; } else { $fileid = sqlite_single_query($GLOBALS['fb_idx'], "SELECT fileid from idents where id='$saferef'"); if ($fileid) { $index = $GLOBALS['fb_idx']; } else { $index = null; } } if ($fileid !== null && $index !== null) { /* determine the first node within that file */ $firstid = sqlite_single_query($index, "SELECT id from idents where fileid='$fileid' LIMIT 1"); } if ($firstid != $ref) { $hash = "#$ref"; } else { $hash = ""; } if ($firstid == 'manual') { $firstid = $ref; $hash = ''; } if (FORCE_DYNAMIC) { $url = "{$_SERVER['PHP_SELF']}?l=$lang&q=$firstid$hash"; } else { $url = WEBBASE . "$lang/$firstid.html$hash"; } $cache[$lang][$ref] = $url; return $url; } function do_nav_line($item, $class, $current_page, $lang, &$fulltitle) { global $current_page_title; $nolink = FALSE; $title = lookup_title($item); $fulltitle = $title; if (strlen($title) > 25) { $ftitle = " title='$title'"; $title = substr($title, 0, 22). '...'; } else { $ftitle = ""; if (!$title) { $title = $item . ' [?]'; $nolink = TRUE; } } if ($item == $current_page) { $current_page_title = $title; $title = "<b>$title</b>"; } $title = str_replace(' ', ' ', $title); if ($nolink) { return "<tr><td class='$class'>$title</td></tr>\n"; } else { $url = generate_url_for_id($lang, $item); return "<tr><td class='$class'><a $ftitle class='$class' href='$url'>$title</a></td></tr>\n"; } } /***************************************************************************** * Search & Replace entities */ function bind_entities($data) { global $idx; static $entity_cache = array(); $entities = array(); $sanity = 0; while (($ent_count = preg_match_all('/&([a-zA-Z0-9.-]+);/sm', $data, $matches)) && $sanity++ < 5) { /* now collect their values */ $entities_to_find = array_unique($matches[1]); foreach ($entities_to_find as $ent) { if (isset($entity_cache[$ent])) { $entities['&' . $ent . ';'] = $entity_cache[$ent]; unset($entities_to_find[$ent]); } } if (count($entities_to_find)) { $ents = implode("','", $entities_to_find); $q = sqlite_query($idx, "SELECT entid, value from ents where is_file=0 and entid in ('" . $ents . "')"); if ($q) { while ($r = sqlite_fetch_array($q, SQLITE_NUM)) { $entities['&' . $r[0] . ';'] = $r[1]; $entities_cache[$r[0]] = $r[1]; } } } if (!count($entities)) break; /* substitute */ $data = strtr($data, $entities); } while ($sanity++ < 5); return $data; } /***************************************************************************** * Helper functions for transformation */ function load_xml_doc($filename, $included = false, $fallback_filename = '', $return_rev = 0) { global $file_revision, $idx, $lang; $replace = array(); $search = array('æ', '©', 'é', 'è', 'à', 'ï', 'ö', 'ä', 'Ä', 'ô', 'ê', 'û', 'î', 'â', 'ë', 'ç', 'ù'); foreach ($search as $item) { $replace[] = html_entity_decode($item); } $lang_rev = 0; $data = @file_get_contents($filename); if (strlen($data) == 0 && strlen($fallback_filename)) { $data = @file_get_contents($fallback_filename); if (strlen($data) == 0) { $data = "<warning>permissions problem for $filename?</warning>"; } } elseif ($lang != 'en' && preg_match('/<!-- EN-Revision: \d+\.(\d+)/', $data, $matches)) { $lang_rev = $matches[1]; } /* get file revision */ if (empty($file_revision) && preg_match('/\$' . 'Revision: [^$]+ \$/', $data, $matches)) { $file_revision .= $matches[0]; } /* strip comments */ $data = preg_replace('@<!--\s+.*\s-->@Usm', '', $data); /* Replace entities */ $data = bind_entities($data); /* catch any undefined entities */ if ($included) { if (basename($filename) == 'functions.xml') { $data = preg_replace('/&([a-zA-Z0-9-]+)\.([a-zA-Z0-9.-]+);/sme', 'make_function_link("\\1.\\2");', $data); } else { $data = preg_replace('/&([a-zA-Z0-9-]+)\.([a-zA-Z0-9.-]+);/sme', 'make_xref("\\1.\\2");', $data); } if (!$data || $data{1} != '?') { $data = '<div>' . $data . '</div>'; } else { $data = preg_replace('@(<\\?xml.*\\?>)@U', '\\1<div>', $data) . '</div>'; } } else { $data = preg_replace('/&([a-zA-Z0-9-]+)\.([a-zA-Z0-9.-]+);/sm', '<phpdoc_include ref="\\1.\\2"/>', $data); } $data = str_replace($search, $replace, $data); $page = new DocBookToHTML($data); if ($return_rev) { return array($page, $lang_rev); } else { return $page; } } function format_user_notes($id) { $notes = sqlite_array_query($GLOBALS['NOTESDB'], "SELECT id, xwhen, who, note from notes where sect='$id' order by xwhen desc"); if (count($notes) == 0) return ''; $inner = <<<HTML <div class="usernotes"> <span class="title">User Contributed Notes</span> HTML; foreach ($notes as $note) { $date = date("d-M-Y h:i", $note['xwhen']); $node = new stdClass; $node->content = $note['note']; $node->attributes['role'] = 'php'; $the_note = format_listing($node); $inner .= <<<HTML <div class="usernote"> <div class="noteheader"> <span class="user">$note[who]</span><br /> <span class="when">$date</span> </div> $the_note </div> HTML; } return $inner . "</div>"; } function lookup_title($nodeid) { static $ids = array(); /* Define your own title mappings here: */ static $special = array ('indexes' => 'FunctionIndex'); /* we're going to do some tricky stuff here for languages, * by using entities as much as possible */ if (isset($special[$nodeid])) { /* Check for the entity value */ if (($r = sqlite_single_query($GLOBALS['idx'], "SELECT entid FROM ents WHERE entid = '$special[$nodeid]'"))) { return $r; } else { /* It's not an entity, let's just return it then */ return $special[$nodeid]; } } /* See if the title lookup is in the cache already */ if (isset($ids[$nodeid])) { return $ids[$nodeid]; } /* It's not, so we try to resolve it from the DB */ $safeid = sqlite_escape_string($nodeid); if (($ids[$nodeid] = sqlite_single_query($GLOBALS['idx'], "SELECT title from idents where id='$safeid'"))) { return $ids[$nodeid]; } else if (isset($GLOBALS['fb_idx']) && ($ids[$nodeid] = sqlite_single_query($GLOBALS['fb_idx'], "SELECT title from idents where id='$safeid'"))) { return $ids[$nodeid]; } return null; } /* transform an entity name of the form "reference.XXXX.functions.ID" * and generate a link to its node using its title */ function make_function_link($ref) { $parts = explode('.', $ref); $stag = 'function'; $etag = 'function'; switch (count($parts)) { case 4: $id = 'function.' . $parts[3]; $func_name = lookup_title($id); break; case 5: if ($parts[3] == 'class') { $id = 'class.' . $parts[4]; $func_name = str_replace('-', '_', $parts[4]); $stag = "xref linkend=\"$id\""; $etag = "xref"; break; } /* fall through */ default: /* some weird node type we don't understand */ $id = $ref; $func_name = 'Unknown ??'; } if ($parts[3] == 'class') { $title = ' - ' . lookup_title($id) . ' class'; } else { $title = ' - ' . bind_entities(sqlite_single_query($GLOBALS['idx'], "SELECT descr from toc where docbook_id='$id'")); } return "<div class=\"function_link\"><$stag>$func_name</$etag>$title</div>"; } function make_xref($ref) { $parts = explode('.', $ref); if ($parts[0] == 'reference' && $parts[2] == 'functions') { $id = 'function.'. $parts[3]; } else { $id = $ref; } $title = lookup_title($id); if (!$title) { $title = $id. ' [?]'; } return "<xref linkend=\"$id\">$title</xref>"; } function do_contents($id, $level) { global $lang; $ret = ''; if (($r = sqlite_array_query($GLOBALS['idx'], "SELECT id, docbook_id FROM toc WHERE parent_id = ".$id, SQLITE_NUM))) { foreach($r as $row) { $ret .= "<li>"; if (!($title = lookup_title($row[1]))) { $ret .= $row[1]. ' [?]'; } else { $ret .= "<a href='".generate_url_for_id($lang, $row[1])."'>".$title."</a>"; } if (($level < 2 && strncmp($row[1], 'ref', 3)) || $level < 1) { $ret .= do_contents($row[0], $level + 1); } $ret .= "</li>\n"; } return "<ul>" . $ret . "</ul>\n"; } return ''; } function handle_include($node) { global $current_page; $ref = $node->attributes['ref']; $curr_lvl = sqlite_single_query($GLOBALS['idx'], "SELECT lvl from toc where docbook_id='$current_page'"); list($row) = sqlite_array_query($GLOBALS['idx'], "SELECT idents.id, lvl from ents left join files on ents.value = files.filename left join idents on files.fileid = idents.fileid left join toc on ents.entid=toc.docbook_id where is_file=1 and ents.entid='$ref' limit 1"); list($id, $lvl) = $row; $filename = sqlite_single_query($GLOBALS['idx'], "SELECT value from ents where entid='$ref' and is_file=1"); $path = PHPDOC . DIRECTORY_SEPARATOR . $filename; if (!file_exists($path)) { $path = PHPDOC . DIRECTORY_SEPARATOR . $GLOBALS['lang'] . DIRECTORY_SEPARATOR . $filename; } if ($lvl > $curr_lvl) { $fake->content = null; $fake->attributes['linkend'] = $id; $fake->tagname = 'xref'; return format_link($fake) . " [$ref, $id, $lvl vs $curr_lvl]<br />"; } $doc = load_xml_doc($path, true, null); return "<!-- $ref : $filename -->" . $doc->transform($GLOBALS['map']); } // Handle the case where a requested node id was not found function handle_missing_index() { global $current_page, $lang; header('Location: ' . WEBBASE . 'search.php?q=' . urlencode($current_page) . '&l=' . $lang); exit; } ?> http://cvs.php.net/co.php/livedocs/pregenerate.php?r=1.1&p=1 Index: livedocs/pregenerate.php +++ livedocs/pregenerate.php <?php /* walk the index and spit out an html page for each item. * Could be modified to spit out .chm bits too. * Unfortunately, PHP 4 and reference assignments leak MASSIVE * amounts of memory if we run the whole thing in a single process. * So, only run this with PHP 5 if you value your RAM! */ define('LIVEDOC_SOURCE', dirname(__FILE__)); include LIVEDOC_SOURCE . '/livedoc_funcs.php'; $ids_and_pages = sqlite_query($idx, "select id, filename from idents left join files on idents.fileid=files.fileid"); $last_file_name = null; chdir(OUTPUTDIR); ob_start(); $start_time = microtime(true); $gen_time = 0; $clean_time = 0; $number_processed = 0; $note_time = 0; $nav_time = 0; $load_time = 0; while ($page_row = sqlite_fetch_array($ids_and_pages)) { if ($last_file_name == $page_row[1]) continue; $number_processed++; $last_file_name = $page_row[1]; $current_page = $page_row[0]; fwrite(STDERR, "Generating $current_page\t"); fflush(STDERR); $ts = microtime(true); list($title, $filename, $fileid, $dirid) = do_nav($idx, $fb_idx, $lang, $current_page, $nav, $children); $tnav = microtime(true) - $ts; $ts = microtime(true); list($page, $lang_rev) = load_xml_doc(BASE . $filename, false, FALLBACK_BASE . $filename, 1); $tload = microtime(true) - $ts; $load_time += $tload; $nav_time += $tnav; ob_clean(); echo manual_page_header(); $ts = microtime(true); echo $page->transform($map, $current_page); $tgen = microtime(true) - $ts; $gen_time += $tgen; if ($NOTESDB) { $tn = microtime(true); fwrite(STDERR, "[notes]\t"); fflush(STDERR); echo format_user_notes($current_page); $gen_note = microtime(true) - $tn; $note_time += $gen_note; } else { $gen_note = 0; } if (empty($file_revision)) { $file_revision = 'unknown revision'; } echo manual_page_footer(); $contents = ob_get_contents(); ob_clean(); $save_file = $lang . "/" . $current_page . ".html"; fwrite(STDERR, "\t$save_file "); fflush(STDERR); $f = fopen($save_file, 'w'); fwrite($f, $contents); fclose($f); fwrite(STDERR, "\n"); fflush(STDERR); $ts = microtime(true); $nodes = $__node_count; /* try to release as much memory as possible */ $page = null; unset($page); unset($nav); unset($children); unset($current_page_title); unset($dirid); unset($fileid); unset($filename); unset($title); unset($file_revision); unset($contents); unset($f); unset($save_file); $tclean = microtime(true) - $ts; $clean_time += $tclean; fprintf(STDERR, "nav %.2fs, load %.2fs, gen %.2fs [note %.2fs], clean(%d) in %.2fs\n\n", $tnav, $tload, $tgen, $gen_note, $nodes, $tclean); if ($__node_count > 0) { fwrite(STDERR, "nodes: $__node_count\n"); fflush(STDERR); } } $elapsed = microtime(true) - $start_time; fprintf(STDERR, "\nProcessed %d pages.\n\nOverall: %.2fs (%.2fs each)\nGeneration: %.2fs (%.2fs each)\nCleaning: %.2fs (%.2fs each)\nNotes: %.2fs (%.2fs each)\nNav: %.2fs (%.2fs each)\nLoad: %.2fs (%.2fs each)\n\n", $number_processed, $elapsed, $elapsed / $number_processed, $gen_time, $gen_time / $number_processed, $clean_time, $clean_time / $number_processed, $nav_time, $nav_time / $number_processed, $load_time, $load_time / $number_processed, $note_time, $note_time / $number_processed ); ?> http://cvs.php.net/co.php/livedocs/xml_classes5.php?r=1.1&p=1 Index: livedocs/xml_classes5.php +++ livedocs/xml_classes5.php <?php /* vim: set tabstop=4 shiftwidth=4: */ // +----------------------------------------------------------------------+ // | PHP version 4 | // +----------------------------------------------------------------------+ // | Copyright (c) 1997-2004 The PHP Group | // +----------------------------------------------------------------------+ // | This source file is subject to version 3.0 of the PHP license, | // | that is bundled with this package in the file LICENSE, and is | // | available through the world-wide-web at the following url: | // | http://www.php.net/license/3_0.txt. | // | If you did not receive a copy of the PHP license and are unable to | // | obtain it through the world-wide-web, please send a note to | // | [EMAIL PROTECTED] so we can mail you a copy immediately. | // +----------------------------------------------------------------------+ // | Authors: Wez Furlong, Derick Rethans, Ilia Alshanetsky | // +----------------------------------------------------------------------+ // | Two XML handling classes for the docbook to html transformation, | // | PHP 5 style | // +----------------------------------------------------------------------+ // // $Id: xml_classes5.php,v 1.1 2004/05/25 11:33:48 wez Exp $ class Node { /* {{{ */ var $tagname = ""; var $attributes = array(); var $children = array(); var $parent = null; var $index = 0; var $content = ""; var $nodeid = 0; function transform($map) { if (strlen($this->tagname) == 0) { return htmlspecialchars($this->content, ENT_NOQUOTES); } $tagname = 'div'; $attributes = $this->attributes; $attributes['class'] = $this->tagname; /* do we match any of the patterns in the map ? */ foreach ($map as $mapent) { if ($mapent[0][0] == $this->tagname) { $n = $this->parent; $match = true; for ($i = 1; $i < count($mapent[0]); $i++) { if ($n->tagname != $mapent[0][$i]) { $match = false; break; } $n = $n->parent; } if ($match) { if (is_callable($mapent[1])) { return call_user_func($mapent[1], $this); } else { $tagname = $mapent[1]; } } } } if (isset($this->attributes['id'])) { $anchor = sprintf('<a name="%s"></a>', $this->attributes['id']); } else { $anchor = ''; } if (strlen($tagname)) { $xml = '<' . $tagname; foreach ($attributes as $name => $value) { $xml .= ' ' . $name . '="' . htmlspecialchars($value) . '"'; } } else { $xml = ''; } $content = ""; if (count($this->children) == 0) { if (strlen($tagname) == 0) { return ''; } if (strlen($this->content)) { $content = htmlspecialchars($this->content, ENT_NOQUOTES); } } else { for ($i = 0; $i < count($this->children); $i++) { $child = $this->children[$i]; $content .= $child->transform($map); } } if (strlen($tagname)) { $xml .= '>'; } if ($tagname == 'th' && strlen($content) == 0) { $content = ' '; } $xml .= $anchor; $xml .= $content; if (strlen($tagname)) { $xml .= '</' . $tagname . '>'; } return $xml; } function select_node($id) { if (isset($this->attributes['id']) && $this->attributes['id'] == $id) { return $node; } // look for children that match for ($i = 0; $i < count($this->children); $i++) { $x = $this->select_node($this->children[$i], $id); if (is_object($x)) { return $x; } } return null; } function Node($tagname, $attributes) { static $nodeid = 0; $this->nodeid = $nodeid++; $this->tagname = $tagname; $this->attributes = $attributes; $GLOBALS['__node_count']++; } function set_parent($parent) { $this->parent = $parent; $this->index = count($parent->children); $parent->children[$this->index] = $this; } function compress() { if (count($this->children) == 1 && $this->children[0]->tagname == '') { $this->content = $this->children[0]->content; $this->children = array(); } } function as_xml() { if (strlen($this->tagname) == 0) { return htmlspecialchars($this->content, ENT_NOQUOTES); } $xml = '<' . $this->tagname; foreach ($this->attributes as $name => $value) { $xml .= ' ' . $name . '="' . htmlspecialchars($value) . '"'; } if (count($this->children) == 0) { if (strlen($this->content) == 0) { $xml .= '/>'; } else { $xml .= '>' . htmlspecialchars($this->content, ENT_NOQUOTES) . '</' . $this->tagname . '>'; } } else { $xml .= '>'; for ($i = 0; $i < count($this->children); $i++) { $xml .= $this->children[$i]->as_xml(); } $xml .= '</' . $this->tagname . '>'; } return $xml; } function add_child($child) { if (count($this->children) == 0 && strlen($this->content)) { if (strlen(trim($this->content)) > 0) { // promote content to an anonymous cdata node $node = new Node("", array()); $node->content = $this->content; $node->set_parent($this); } $this->content = ""; } $child->set_parent($this); } function add_cdata($data) { if (count($this->children) == 0) { $this->content .= $data; } else { $node = new Node("", array()); $node->content = $data; $this->add_child($node); } } function __destruct() { $GLOBALS['__node_count']--; } function release() { for ($i = 0; $i < count($this->children); $i++) { if (is_object($this->children[$i])) { $this->children[$i]->release(); } } for ($i = 0; $i < count($this->children); $i++) { $this->children[$i] = null; } $this->parent = null; } } /* }}} */ class DocBookToHTML { /* {{{ */ var $html = ""; var $top = null; var $current = null; function __destruct() { if (is_object($this->top)) { $this->top->release(); } if (is_object($this->current)) { $this->current->release(); } $this->top = null; unset($this->top); $this->current = null; unset($this->current); } function DocBookToHTML($xml, $id = null) { if (strlen($xml) == 0) { return; } $p = xml_parser_create(); xml_set_object($p, $this); xml_parser_set_option($p, XML_OPTION_CASE_FOLDING, 0); xml_set_element_handler($p, 'start_elem', 'end_elem'); xml_set_character_data_handler($p, 'cdata'); if (!xml_parse($p, $xml, true)) { printf("XML: %d:%d %s\n", xml_get_current_line_number($p), xml_get_current_column_number($p), xml_error_string(xml_get_error_code($p)) ); $lines = explode("\n", $xml); $l = xml_get_current_line_number($p); echo "\nLine: $l is <b>" . htmlentities($lines[$l-1]) . "</b><br />\n"; echo '<pre>'; echo htmlentities($xml); echo '</pre>'; } xml_parser_free($p); if ($id !== null) { $newtop = $this->top->select_node($id); if (is_object($newtop)) { $this->top = $newtop; $this->top->parent = null; } } } /* apply a transformation map to content */ function transform($map) { /* pre-parse the map */ if (!is_object($this->top)) { return "<div class=\"warning\">XML document contained no data</div>"; } $pmap = array(); foreach ($map as $pat => $rep) { $h = array_reverse(explode('/', $pat)); $pmap[] = array($h, $rep); } return $this->top->transform($pmap); } /* returns structure as XML */ function as_xml() { return $this->_as_xml($this->top); } function _as_xml($node) { if (strlen($node->tagname) == 0) { return htmlspecialchars($node->content, ENT_NOQUOTES); } $xml = '<' . $node->tagname; foreach ($node->attributes as $name => $value) { $xml .= ' ' . $name . '="' . htmlspecialchars($value) . '"'; } if (count($node->children) == 0) { if (strlen($node->content) == 0) { $xml .= '/>'; } else { $xml .= '>' . htmlspecialchars($node->content, ENT_NOQUOTES) . '</' . $node->tagname . '>'; } } else { $xml .= '>'; for ($i = 0; $i < count($node->children); $i++) { $xml .= $this->_as_xml($node->children[$i]); } $xml .= '</' . $node->tagname . '>'; } return $xml . "<!-- {$node->nodeid} -->"; } function start_elem($parser, $tagname, $attributes) { /* create a new node as a child of the current node */ $node = new Node($tagname, $attributes); if ($this->top === null) { /* set the top node */ $this->top = $node; $this->current = $node; } else { /* child of current */ $this->current->add_child($node); $this->current = $node; } } function end_elem($parser, $tagname) { /* pop up to parent of the current node */ if ($this->current === null) { return; } /* optimize the node; if it only contains a single cdata (anonymous) node, * compress that node into the content */ $this->current->compress(); $this->current = $this->current->parent; } function cdata($parser, $data) { $this->current->add_cdata($data); } } /* }}} */ ?>