1+ <?php
2+ /*
3+ +----------------------------------------------------------------------+
4+ | Copyright (c) 1997-2023 The PHP Group |
5+ +----------------------------------------------------------------------+
6+ | This source file is subject to version 3.01 of the PHP license, |
7+ | that is bundled with this package in the file LICENSE, and is |
8+ | available through the world-wide-web at the following url: |
9+ | https://www.php.net/license/3_01.txt. |
10+ | If you did not receive a copy of the PHP license and are unable to |
11+ | obtain it through the world-wide-web, please send a note to |
12+ | license@php.net, so we can mail you a copy immediately. |
13+ +----------------------------------------------------------------------+
14+ | Authors: André L F S Bacci <ae php.net> |
15+ +----------------------------------------------------------------------+
16+ | Description: Collect individual entities into an entities.ent file. |
17+ +----------------------------------------------------------------------+
18+
19+ # Conventions
20+
21+ * `.dnt`: Simple text, "do not translate" file;
22+ * `.txt`: Simple text, translatable, untracked file;
23+ * `.xml`: Full XML, translatable, tracked file.
24+
25+ Each entitiesDir is read in order, overwriting previous defined
26+ entities with new ones (this is inverse of XML processing, where
27+ overwriting entities are ignored).
28+ */
29+
30+ if ( count ( $ argv ) < 2 || in_array ( '--help ' , $ argv ) || in_array ( '-h ' , $ argv ) )
31+ {
32+ fwrite ( STDERR , "\nUsage: {$ argv [0 ]} entitiesDir [entitiesDir] \n\n" );
33+ return ;
34+ }
35+
36+ $ generate = realpath ( __DIR__ . "/../.entities.ent " ); // sibling of .manual.xml
37+ $ entities = []; // all entitites, already overriden
38+ $ expected = []; // entities that are expected to be oversidem (translatins)
39+ $ override = []; // overrides stattics
40+
41+ $ langs = [];
42+ $ detail = false ;
43+
44+ for ( $ idx = 1 ; $ idx < count ( $ argv ) ; $ idx ++ )
45+ if ( $ argv [$ idx ] == "--detail " )
46+ $ detail = true ;
47+ else
48+ $ langs [] = $ argv [$ idx ];
49+
50+ if ( $ detail )
51+ print "Creating file $ generate in verbose detail mode... \n" ;
52+ else
53+ print "Creating file $ generate... " ;
54+
55+ for ( $ run = 0 ; $ run < count ( $ langs ) ; $ run ++ )
56+ parseDir ( $ langs [$ run ] , $ run > 0 );
57+
58+ dump ( $ entities );
59+
60+ [$ count , $ untranslated , $ overriden ] = verifyOverrides ( $ detail );
61+
62+ if ( $ detail )
63+ {
64+ print "Done. \n" ;
65+ }
66+ else
67+ {
68+ echo " done " ;
69+ if ( $ untranslated + $ overriden > 0 )
70+ echo ": $ count entities, $ untranslated untranslated, $ overriden orerriden " ;
71+ echo ". \n" ;
72+ }
73+ exit ;
74+
75+
76+
77+ function parseDir ( string $ dir , bool $ expectedOverride )
78+ {
79+ if ( ! is_dir ( $ dir ) )
80+ return ; // for now. When implanted in all languages: exit( "Not a directory: $dir\n" );
81+
82+ $ files = scandir ( $ dir );
83+
84+ foreach ( $ files as $ file )
85+ {
86+ if ( str_starts_with ( $ file , '. ' ) )
87+ continue ;
88+
89+ $ path = realpath ( "$ dir/ $ file " );
90+
91+ if ( is_dir ( $ path ) )
92+ continue ;
93+
94+ $ text = file_get_contents ( $ path );
95+
96+ if ( validate ( $ path , $ text , false ) )
97+ {
98+ push ( $ path , $ text , $ expectedOverride );
99+ continue ;
100+ }
101+
102+ $ frag = "<frag> $ text</frag> " ;
103+ if ( validate ( $ path , $ frag , true ) )
104+ {
105+ push ( $ path , $ text , $ expectedOverride );
106+ continue ;
107+ }
108+ }
109+ }
110+
111+ function validate ( string $ path , string $ text , bool $ warn ) : bool
112+ {
113+ $ dom = new DOMDocument ( '1.0 ' , 'utf8 ' );
114+ $ dom ->recover = true ;
115+ $ dom ->resolveExternals = false ;
116+ libxml_use_internal_errors ( true );
117+
118+ $ res = $ dom ->loadXML ( $ text );
119+
120+ $ err = libxml_get_errors ();
121+ libxml_clear_errors ();
122+
123+ foreach ( $ err as $ item )
124+ {
125+ $ msg = $ item ->message ;
126+ if ( str_starts_with ( $ msg , "Entity ' " ) && str_ends_with ( $ msg , "' not defined " ) )
127+ continue ;
128+
129+ if ( $ warn )
130+ {
131+ fwrite ( STDERR , "\n XML load failed on entity file. " );
132+ fwrite ( STDERR , "\n Path: $ path " );
133+ fwrite ( STDERR , "\n Error: $ msg \n" );
134+ }
135+ return false ;
136+ }
137+ return true ;
138+ }
139+
140+ function push ( string $ path , string $ contents , bool $ expectedOverride )
141+ {
142+ global $ entities ;
143+ global $ expected ;
144+ global $ override ;
145+
146+ $ info = pathinfo ( $ path );
147+ $ name = $ info ["filename " ];
148+
149+ if ( $ expectedOverride )
150+ $ expected [] = $ name ;
151+
152+ if ( ! isset ( $ override [$ name ] ) )
153+ $ override [$ name ] = 0 ;
154+ else
155+ $ override [$ name ]++;
156+
157+ $ entities [$ name ] = $ contents ;
158+ }
159+
160+ function dump ( array $ entities )
161+ {
162+ global $ generate ;
163+
164+ // In PHP 8.4 may be possible to construct an extended
165+ // DOMEntity class with writable properties. For now,
166+ // creating entities files directly as text.
167+
168+ $ file = fopen ( $ generate , "w " );
169+ fputs ( $ file , "\n<!-- DO NOT COPY - Autogenerated by entities.php --> \n\n" );
170+
171+ foreach ( $ entities as $ name => $ text )
172+ {
173+ $ text = str_replace ( "' " , '' ' , $ text );
174+ fputs ( $ file , "<!ENTITY $ name ' $ text'> \n\n" );
175+ }
176+ fclose ( $ file );
177+ }
178+
179+ function verifyOverrides ( bool $ outputDetail )
180+ {
181+ global $ entities ;
182+ global $ expected ;
183+ global $ override ;
184+
185+ $ countGenerated = count ( $ entities );
186+ $ countExpectedOverriden = 0 ;
187+ $ countUnexpectedOverriden = 0 ;
188+
189+ foreach ( $ entities as $ name => $ text )
190+ {
191+ $ times = $ override [$ name ];
192+
193+ if ( isset ( $ expected [$ name ] ) )
194+ {
195+ if ( $ times != 1 )
196+ {
197+ $ countExpectedOverriden ++;
198+ if ( $ outputDetail )
199+ print "Expected override entity $ name overriden $ times times. \n" ;
200+ }
201+ }
202+ else
203+ {
204+ if ( $ times != 0 )
205+ {
206+ $ countUnexpectedOverriden ++;
207+ if ( $ outputDetail )
208+ print "Unexpected override entity $ name overriden $ times times. \n" ;
209+ }
210+ }
211+ }
212+
213+ return [$ countGenerated , $ countExpectedOverriden , $ countUnexpectedOverriden ];
214+ }
0 commit comments