Skip to content

Commit 984e433

Browse files
author
André L F S Bacci
committed
XInclude by xml:id
1 parent dfcbb86 commit 984e433

File tree

2 files changed

+130
-28
lines changed

2 files changed

+130
-28
lines changed

configure.php

Lines changed: 106 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -775,46 +775,108 @@ function getFileModificationHistory(): array {
775775
echo "done.\n";
776776

777777
echo "Running XInclude/XPointer... ";
778-
$total = 0;
779-
$maxrun = 10; //LIBXML_VERSION >= 21100 ? 1 : 10;
780-
for( $run = 0 ; $run < $maxrun ; $run++ )
778+
779+
$total = xinclude_run_byid( $dom );
780+
$total += xinclude_run_xpointer( $dom );
781+
782+
if ( $total == 0 )
783+
echo "failed.\n";
784+
else
785+
echo "done. Performed $total XIncludes.\n";
786+
787+
xinclude_report();
788+
xinclude_residual( $dom );
789+
790+
function xinclude_run_byid( DOMDocument $dom )
781791
{
782-
if ( $run > 0 )
783-
echo "$run ";
784-
libxml_clear_errors();
785-
$status = (int) $dom->xinclude();
786-
if ( $status <= 0 )
787-
break;
788-
$total += $status;
789-
if ( $maxrun > 1 && $run + 1 >= $maxrun )
792+
$total = 0;
793+
$maxrun = 10; //LIBXML_VERSION >= 21100 ? 1 : 10;
794+
for( $run = 0 ; $run < $maxrun ; $run++ )
790795
{
791-
echo "Recursive XInclude is too deep.\n";
792-
errors_are_bad(-1);
796+
echo "$run ";
797+
$xpath = new DOMXPath( $dom );
798+
$xpath->registerNamespace( "xi" , "http://www.w3.org/2001/XInclude" );
799+
$xincludes = $xpath->query( "//xi:include" );
800+
801+
$progress = false;
802+
foreach( $xincludes as $xinclude )
803+
{
804+
$xpointer = $xinclude->getAttribute( "xpointer" );
805+
$target = $xinclude->ownerDocument->getElementById( $xpointer );
806+
807+
if ( $target == null )
808+
continue;
809+
810+
$other = new DOMDocument( '1.0' , 'utf8' );
811+
$frags = $other->createDocumentFragment();
812+
$other->append( $frags );
813+
$frags->append( $other->importNode( $target , true ) ); // dup add
814+
815+
// "attributes in xml: namespace are not copied"
816+
817+
$oxpth = new DOMXPath( $other );
818+
$attribs = $oxpth->query( "//@*" );
819+
820+
foreach( $attribs as $attrib )
821+
if ( $attrib->prefix == "xml" )
822+
$attrib->parentNode->removeAttribute( $attrib->nodeName );
823+
824+
$insert = $dom->importNode( $frags , true ); // dup
825+
$xinclude->parentNode->insertBefore( $insert , $xinclude ); // add
826+
$xinclude->parentNode->removeChild( $xinclude ); // del
827+
828+
$progress = true;
829+
$total++;
830+
}
831+
832+
if ( $progress )
833+
continue;
834+
else
835+
return $total;
793836
}
837+
echo "XInclude nested too deeply (xml:id).\n";
838+
errors_are_bad( -1 );
794839
}
795840

796-
if ($total == 0) {
797-
echo "failed.\n";
798-
} else {
799-
echo "done. Performed $total XIncludes.\n";
841+
function xinclude_run_xpointer( DOMDocument $dom ) : int
842+
{
843+
$total = 0;
844+
$maxrun = 10; //LIBXML_VERSION >= 21100 ? 1 : 10;
845+
for( $run = 0 ; $run < $maxrun ; $run++ )
846+
{
847+
echo "$run ";
848+
libxml_clear_errors();
849+
$status = (int) $dom->xinclude();
850+
if ( $status <= 0 )
851+
return $total;
852+
$total += $status;
853+
}
854+
echo "XInclude nested too deeply (xpointer).\n";
855+
errors_are_bad( -1 );
800856
}
801-
flush();
802857

803-
if ( $ac['XPOINTER_REPORTING'] == 'yes' || $ac['LANG'] == 'en' )
858+
function xinclude_report()
804859
{
860+
global $ac;
861+
862+
$report = $ac['XPOINTER_REPORTING'] == 'yes' || $ac['LANG'] == 'en';
863+
$output = $ac['STDERR_TO_STDOUT'] == 'yes' ? STDOUT : STDERR;
864+
$fatal = $ac['LANG'] == 'en';
865+
805866
$errors = libxml_get_errors();
806-
$output = ( $ac['STDERR_TO_STDOUT'] == 'yes' ) ? STDOUT : STDERR;
807-
if ( count( $errors ) > 0 )
867+
libxml_clear_errors();
868+
869+
if ( count( $errors ) > 0 && $report )
808870
{
809-
fprintf( $output , "\n");
871+
fprintf( $output , "\n\n");
810872
foreach( $errors as $error )
811-
fprintf( $output , "{$error->message}\n");
812-
if ( $ac['LANG'] == 'en' )
813-
errors_are_bad(1);
873+
fprintf( $output , rtrim( $error->message ) . "\n\n" );
874+
if ( $fatal )
875+
errors_are_bad( 1 );
814876
}
815877
}
816878

817-
if ( $ac['LANG'] != 'en' )
879+
function xinclude_residual( DOMDocument $dom )
818880
{
819881
// XInclude failures are soft errors on translations, so remove
820882
// residual XInclude tags on translations to keep them building.
@@ -844,7 +906,7 @@ function getFileModificationHistory(): array {
844906
// $fixup = "<varlistentry><term>></term><listitem><simpara></simpara></listitem></varlistentry>";
845907
// break;
846908
default:
847-
echo "Unknown parent element of failed XInclude: $tagName\n";
909+
echo "Unknown parent of failed XInclude: $tagName\n";
848910
$explain = true;
849911
continue 2;
850912
}
@@ -869,6 +931,23 @@ function getFileModificationHistory(): array {
869931
MSG;
870932
exit(-1); // stop here, do not let more messages further confuse the matter
871933
}
934+
935+
// XInclude by xml:id never duplicates xml:id, horever, also using
936+
// XInclude by XPath/XPointer may start causing duplications
937+
// (see docs/structure.md). Crude and ugly fixup ahead, beware!
938+
939+
$list = array();
940+
$nodes = $xpath->query( "//*[@xml:id]" );
941+
foreach( $nodes as $node )
942+
{
943+
$id = $node->getAttribute( "xml:id" );
944+
if ( isset( $list[ $id ] ) )
945+
{
946+
echo " Random removing duplicated xml:id: $id\n";
947+
$node->removeAttribute( "xml:id" );
948+
}
949+
$list[ $id ] = $id;
950+
}
872951
}
873952

874953
echo "Validating {$ac["INPUT_FILENAME"]}... ";
@@ -973,4 +1052,3 @@ function getFileModificationHistory(): array {
9731052

9741053
errors_are_bad(1); // Tell the shell that this script finished with an error.
9751054
}
976-
?>

docs/structure.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,27 @@ There are some other important files:
4242
Including common warnings, notes, etc.
4343
- *translation.xml* - this file is used to store all central translation info, like a small
4444
intro text for translators and the persons list. This file is not present in the English tree.
45+
46+
## `xml:id` structure
47+
48+
The PHP Manual is a complex document that uses a lot of `xml:id` for anchoring,
49+
linking and XInclude purposes, so some care is necessary when dealing with
50+
them. The pseudo-types of `xml:id` used in manual are:
51+
52+
* **Structural IDs**. IDs that are defined in structural level DocBook
53+
elements, like <chapter>`, `<section>`, etc.
54+
55+
* **XInclude IDs**. IDs that are defined in some other elements, to be targeted
56+
by `<xi:include>` functionality.
57+
58+
Structural IDs use the `id.id` pattern, while XInclude IDs use the
59+
`structural.id..local.ṕath` pattern. That is, Structural IDs only use one dot
60+
as separator, while XInclude IDs are composed of the one existing Structural ID
61+
as prefix, an `..` separator, and a local path suffix.
62+
63+
No `xml:id` can be defined twice in source XMLs. Yet, it is possible that
64+
XInclude functionality generates duplicated IDs while building manuals, as
65+
libxml2 does *not* implement XIncludes 1.1. The `configure.php` script strips
66+
these generated duplicated IDs automatically, but manual editors should strive
67+
to avoid generated duplicated IDs by using XInclude that `xpointer`to XInclude
68+
IDs instead of proper XPointer/XPaths.

0 commit comments

Comments
 (0)