From 16aca1ef59cba608f264ef27bde8862f112830e8 Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Tue, 21 Jul 2026 21:05:05 +0200 Subject: [PATCH 1/4] Read acronyms from translatable acronym.* entities --- phpdotnet/phd/Package/PHP/XHTML.php | 52 +++- render.php | 2 +- tests/package/php/acronym_info_001.phpt | 23 ++ tests/package/php/data/acronyms.ent | 16 ++ tests/package/php/data/acronyms.xml | 368 ------------------------ 5 files changed, 77 insertions(+), 384 deletions(-) create mode 100644 tests/package/php/acronym_info_001.phpt create mode 100644 tests/package/php/data/acronyms.ent delete mode 100644 tests/package/php/data/acronyms.xml diff --git a/phpdotnet/phd/Package/PHP/XHTML.php b/phpdotnet/phd/Package/PHP/XHTML.php index 9e83ed256..11caabf63 100644 --- a/phpdotnet/phd/Package/PHP/XHTML.php +++ b/phpdotnet/phd/Package/PHP/XHTML.php @@ -341,26 +341,48 @@ public static function generateAcronymInfo($filename) { return array(); } - $r = new \XMLReader; - if (!$r->open($filename)) { + $subset = file_get_contents($filename); + if ($subset === false) { throw new \Error(vsprintf('Could not open file for accessing acronym information (%s)', [$filename])); } - $acronyms = array(); - $k = ""; - while ($r->read()) { - if ($r->nodeType != \XMLReader::ELEMENT) { - continue; - } - if ($r->name == "term") { - $r->read(); - $k = $r->value; - $acronyms[$k] = ""; - } else if ($r->name == "simpara") { - $r->read(); - $acronyms[$k] = $r->value; + // libxml only exposes entity content once referenced, so collect + // the acronym.* entity names first, then reference each of them. + $useInternalErrors = libxml_use_internal_errors(true); + $dom = new \DOMDocument; + $loaded = $dom->loadXML(""); + if (!$loaded) { + libxml_clear_errors(); + libxml_use_internal_errors($useInternalErrors); + trigger_error(vsprintf("Can't parse acronym file (%s), skipping", [$filename]), E_USER_WARNING); + return []; + } + + $names = []; + foreach ($dom->doctype->entities as $entity) { + if (strncmp($entity->nodeName, "acronym.", 8) === 0) { + $names[] = $entity->nodeName; } } + + $body = ""; + foreach ($names as $name) { + $body .= '&' . $name . ';'; + } + $dom = new \DOMDocument; + $loaded = $dom->loadXML("" . $body . "", LIBXML_NOENT); + libxml_clear_errors(); + libxml_use_internal_errors($useInternalErrors); + if (!$loaded) { + trigger_error(vsprintf("Can't expand acronym entities (%s), skipping", [$filename]), E_USER_WARNING); + return []; + } + + $acronyms = []; + foreach ($dom->documentElement->childNodes as $node) { + $acronyms[substr($node->getAttribute("name"), 8)] = trim(preg_replace('/\s+/', ' ', $node->textContent)); + } + ksort($acronyms); $info = $acronyms; return $acronyms; } diff --git a/render.php b/render.php index 94c9e0967..7ef4ae02c 100644 --- a/render.php +++ b/render.php @@ -66,7 +66,7 @@ . "phd" . DIRECTORY_SEPARATOR . "data" . DIRECTORY_SEPARATOR . "langs" . DIRECTORY_SEPARATOR, "phpwebVersionFilename" => $config->xmlRoot . DIRECTORY_SEPARATOR . 'version.xml', - "phpwebAcronymFilename" => $config->xmlRoot . DIRECTORY_SEPARATOR . 'entities' . DIRECTORY_SEPARATOR . 'acronyms.xml', + "phpwebAcronymFilename" => $config->xmlRoot . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . 'entities.ent', "phpwebSourcesFilename" => $config->xmlRoot . DIRECTORY_SEPARATOR . 'sources.xml', "phpwebHistoryFilename" => $config->xmlRoot . DIRECTORY_SEPARATOR . 'fileModHistory.php', )); diff --git a/tests/package/php/acronym_info_001.phpt b/tests/package/php/acronym_info_001.phpt new file mode 100644 index 000000000..a889d33f7 --- /dev/null +++ b/tests/package/php/acronym_info_001.phpt @@ -0,0 +1,23 @@ +--TEST-- +Acronym info 001 - Acronyms are read from acronym.* XML entities +--FILE-- + +--EXPECT-- +array(4) { + ["API"]=> + string(33) "Application Programming Interface" + ["CSPRNG"]=> + string(54) "Cryptographically Secure PseudoRandom Number Generator" + ["PHP"]=> + string(27) "PHP: Hypertext Preprocessor" + ["cURL"]=> + string(18) "Client URL Library" +} diff --git a/tests/package/php/data/acronyms.ent b/tests/package/php/data/acronyms.ent new file mode 100644 index 000000000..53eda86bf --- /dev/null +++ b/tests/package/php/data/acronyms.ent @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/tests/package/php/data/acronyms.xml b/tests/package/php/data/acronyms.xml deleted file mode 100644 index 0120d0892..000000000 --- a/tests/package/php/data/acronyms.xml +++ /dev/null @@ -1,368 +0,0 @@ - - - - -
- Acronyms - - - API - - Application Programming Interface - - - - ASCII - - American Standard Code for Information Interchange - - - - BMP - - Bitmap - - - - CGI - - Common Gateway Interface - - - - CLI - - Command Line Interpreter/Interface - - - - CSS - - Cascading Style Sheets - - - - CSV - - Comma Separated Value - - - - CVS - - Concurrent Versions System - - - - DLL - - Dynamic Link Library - - - - DSSSL - - Document Style Semantics and Specification Language - - - - EXIF - - Exchangeable Image File - - - - EOF - - End Of File - - - - FAQ - - Frequently Asked Questions - - - - FTP - - File Transfer Protocol - - - - GD - - Graphics Draw - - - - GIF - - Graphic Interchange Format - - - - GMP - - GNU Multiple Precision - - - - HIS - - Hyperwave Information Server - - - - HTML - - Hyper Text Markup Language - - - - HTTP - - Hypertext Transfer Protocol - - - - IDE - - Integrated Development Environment - - - - IFD - - Image File Directory - - - - IFF - - Interchange File Format - - - - IMAP - - Internet Message Access Protocol - - - - JB2 - - Joint Bilevel Image Experts Group - - - - JP2 - - JPEG-2000 - - - - JPC - - Japan Picture - - - - JPEG - - Joint Photographic Experts Group - - - - MIME - - Multipurpose Internet Mail Extensions - - - - MP - - Multiple Precision - - - - MSDN - - Microsoft Developer Network - - - - NNTP - - Network News Transfer Protocol - - - - PDF - - Portable Document Format - - - - PECL - - PHP Extension and Application Repository - - - - PHP - - PHP: Hypertext Preprocessor - - - - PNG - - Portable Network Graphics - - - - POP3 - - Post Office Protocol version 3 - - - - POSIX - - Portable Operating System Interface - - - - PSD - - Photoshop Document - - - - RGB - - Red-Green-Blue - - - - SAPI - - Server Application Programming Interface - - - - SASL - - Simple Authentication and Security Layer - - - - SWF - - Shockwave Flash - - - - TCP - - Transmission Control Protocol - - - - TIFF - - Tagged Image File Format - - - - TNS - - Transparent Network Substrate - - - - UDF - - User Defined Functions - - - - Unicode - - Unique, Universal, and Uniform character enCoding - - - - URI - - Uniform Resource Identifier - - - - URL - - Uniform Resource Locator - - - - WBMP - - Wireless Bitmap - - - - XBM - - X Bitmap - - - - XML - - eXtensible Markup Language - - - - XPath - - XML Path Language - - - - XSLT - - eXtensible Stylesheet Language Transformations - - - - -
- - From 55603b65b50ebec7ec94566d571490ea8c6be1ed Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Wed, 22 Jul 2026 11:38:24 +0200 Subject: [PATCH 2/4] Apply review Co-authored-by: Louis-Arnaud --- phpdotnet/phd/Package/PHP/XHTML.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/phpdotnet/phd/Package/PHP/XHTML.php b/phpdotnet/phd/Package/PHP/XHTML.php index 11caabf63..a49fa6519 100644 --- a/phpdotnet/phd/Package/PHP/XHTML.php +++ b/phpdotnet/phd/Package/PHP/XHTML.php @@ -380,7 +380,8 @@ public static function generateAcronymInfo($filename) { $acronyms = []; foreach ($dom->documentElement->childNodes as $node) { - $acronyms[substr($node->getAttribute("name"), 8)] = trim(preg_replace('/\s+/', ' ', $node->textContent)); + $acronym = substr($node->getAttribute("name"), strlen("acronym.")); + $acronyms[$acronym] = trim(preg_replace('/\s+/', ' ', $node->textContent)); } ksort($acronyms); $info = $acronyms; From c3c6dcb67266993718fc6452b865ba1529896fc6 Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Wed, 22 Jul 2026 11:38:31 +0200 Subject: [PATCH 3/4] Apply review Co-authored-by: Louis-Arnaud --- phpdotnet/phd/Package/PHP/XHTML.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpdotnet/phd/Package/PHP/XHTML.php b/phpdotnet/phd/Package/PHP/XHTML.php index a49fa6519..1608bfd5c 100644 --- a/phpdotnet/phd/Package/PHP/XHTML.php +++ b/phpdotnet/phd/Package/PHP/XHTML.php @@ -360,7 +360,7 @@ public static function generateAcronymInfo($filename) { $names = []; foreach ($dom->doctype->entities as $entity) { - if (strncmp($entity->nodeName, "acronym.", 8) === 0) { + if (str_starts_with($entity->nodeName, "acronym.")) { $names[] = $entity->nodeName; } } From e059fdc822832dafadd4acc2839af38265daabc6 Mon Sep 17 00:00:00 2001 From: Jordi Kroon Date: Wed, 22 Jul 2026 21:32:57 +0200 Subject: [PATCH 4/4] simplify acronym parsing with SimpleXML --- phpdotnet/phd/Package/PHP/XHTML.php | 44 +++++--------------- render.php | 13 +++++- tests/package/php/acronym_info_001.phpt | 4 +- tests/package/php/data/acronyms.ent | 16 ------- tests/package/php/data/entities.acronyms.ent | 23 ++++++++++ 5 files changed, 46 insertions(+), 54 deletions(-) delete mode 100644 tests/package/php/data/acronyms.ent create mode 100644 tests/package/php/data/entities.acronyms.ent diff --git a/phpdotnet/phd/Package/PHP/XHTML.php b/phpdotnet/phd/Package/PHP/XHTML.php index 1608bfd5c..53a78c9c6 100644 --- a/phpdotnet/phd/Package/PHP/XHTML.php +++ b/phpdotnet/phd/Package/PHP/XHTML.php @@ -336,53 +336,29 @@ public static function generateAcronymInfo($filename) { if ($info) { return $info; } + if (!is_file($filename)) { trigger_error(vsprintf("Can't find acronym file (%s), skipping", [$filename]), E_USER_WARNING); return array(); } - $subset = file_get_contents($filename); - if ($subset === false) { - throw new \Error(vsprintf('Could not open file for accessing acronym information (%s)', [$filename])); - } - - // libxml only exposes entity content once referenced, so collect - // the acronym.* entity names first, then reference each of them. $useInternalErrors = libxml_use_internal_errors(true); - $dom = new \DOMDocument; - $loaded = $dom->loadXML(""); - if (!$loaded) { - libxml_clear_errors(); - libxml_use_internal_errors($useInternalErrors); - trigger_error(vsprintf("Can't parse acronym file (%s), skipping", [$filename]), E_USER_WARNING); - return []; - } - - $names = []; - foreach ($dom->doctype->entities as $entity) { - if (str_starts_with($entity->nodeName, "acronym.")) { - $names[] = $entity->nodeName; - } - } - - $body = ""; - foreach ($names as $name) { - $body .= '&' . $name . ';'; - } - $dom = new \DOMDocument; - $loaded = $dom->loadXML("" . $body . "", LIBXML_NOENT); + $entities = simplexml_load_file($filename); libxml_clear_errors(); libxml_use_internal_errors($useInternalErrors); - if (!$loaded) { - trigger_error(vsprintf("Can't expand acronym entities (%s), skipping", [$filename]), E_USER_WARNING); + if ($entities === false) { + trigger_error(vsprintf("Can't parse acronym file (%s), skipping", [$filename]), E_USER_WARNING); return []; } $acronyms = []; - foreach ($dom->documentElement->childNodes as $node) { - $acronym = substr($node->getAttribute("name"), strlen("acronym.")); - $acronyms[$acronym] = trim(preg_replace('/\s+/', ' ', $node->textContent)); + foreach ($entities as $entity) { + $name = (string) $entity["name"]; + if (str_starts_with($name, "acronym.expansion.")) { + $acronyms[substr($name, strlen("acronym.expansion."))] = trim(preg_replace('/\s+/', ' ', (string) $entity)); + } } + ksort($acronyms); $info = $acronyms; return $acronyms; diff --git a/render.php b/render.php index 7ef4ae02c..15f8c7b87 100644 --- a/render.php +++ b/render.php @@ -61,12 +61,21 @@ // This needs to be moved. Preferably into the PHP package. if (!$conf) { + // Acronym expansions come from the translatable entity file in the + // language checkout (e.g. en/, it/), languages without a + // translated copy fall back to the English one. + $acronymFilename = dirname($config->xmlRoot) . DIRECTORY_SEPARATOR . $config->language + . DIRECTORY_SEPARATOR . 'entities' . DIRECTORY_SEPARATOR . 'entities.acronyms.ent'; + if (!is_file($acronymFilename)) { + $acronymFilename = dirname($config->xmlRoot) . DIRECTORY_SEPARATOR . 'en' + . DIRECTORY_SEPARATOR . 'entities' . DIRECTORY_SEPARATOR . 'entities.acronyms.ent'; + } $config->init(array( "langDir" => __INSTALLDIR__ . DIRECTORY_SEPARATOR . "phpdotnet" . DIRECTORY_SEPARATOR . "phd" . DIRECTORY_SEPARATOR . "data" . DIRECTORY_SEPARATOR . "langs" . DIRECTORY_SEPARATOR, "phpwebVersionFilename" => $config->xmlRoot . DIRECTORY_SEPARATOR . 'version.xml', - "phpwebAcronymFilename" => $config->xmlRoot . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . 'entities.ent', + "phpwebAcronymFilename" => $acronymFilename, "phpwebSourcesFilename" => $config->xmlRoot . DIRECTORY_SEPARATOR . 'sources.xml', "phpwebHistoryFilename" => $config->xmlRoot . DIRECTORY_SEPARATOR . 'fileModHistory.php', )); @@ -108,7 +117,7 @@ $outputHandler->v("Indexing...", VERBOSE_INDEXING); // Create indexer $format = new Index($config->indexCache, $config, $outputHandler); - + $render->attach($format); $outputHandler->v("Running full build", VERBOSE_RENDER_STYLE); diff --git a/tests/package/php/acronym_info_001.phpt b/tests/package/php/acronym_info_001.phpt index a889d33f7..b015243d2 100644 --- a/tests/package/php/acronym_info_001.phpt +++ b/tests/package/php/acronym_info_001.phpt @@ -1,12 +1,12 @@ --TEST-- -Acronym info 001 - Acronyms are read from acronym.* XML entities +Acronym info 001 - Acronyms are read from acronym.expansion.* entity elements --FILE-- diff --git a/tests/package/php/data/acronyms.ent b/tests/package/php/data/acronyms.ent deleted file mode 100644 index 53eda86bf..000000000 --- a/tests/package/php/data/acronyms.ent +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - diff --git a/tests/package/php/data/entities.acronyms.ent b/tests/package/php/data/entities.acronyms.ent new file mode 100644 index 000000000..a657a3cff --- /dev/null +++ b/tests/package/php/data/entities.acronyms.ent @@ -0,0 +1,23 @@ + + + + + +Some other, non acronym, entity + +PHP + +Application Programming Interface + + + Cryptographically Secure + PseudoRandom Number Generator + + +PHP: Hypertext Preprocessor + +Client URL Library + +