@@ -118,6 +118,139 @@ public function genRoutes(): bool
118118 return true ;
119119 }
120120
121+ public function genDocs (string $ targetDir ): bool
122+ {
123+ foreach ($ this ->sourceDirs as $ dir ) {
124+ if (! is_readable ($ dir )) {
125+ echo "[E] Source directory isn't readable: " . $ dir . "\n" ;
126+ return false ;
127+ }
128+ }
129+
130+ /* Iterate over source directories and their files (don't do so recursively,
131+ the OpenBroadcaster framework doesn't support this for the core files anyway,
132+ so each directory with source code has to be added to the source directories
133+ array). Make sure all files are parsed before outputting any HTML, as we need
134+ to know about all the files to be able to output the navigation sidebar. */
135+ echo "Parsing PHP files and generating documentation structure. \n" ;
136+ $ pages = [];
137+ $ doc_files = [];
138+ foreach ($ this ->sourceDirs as $ dir ) {
139+ foreach (new \FilesystemIterator ($ dir ) as $ file ) {
140+ if ($ file ->isDir ()) {
141+ continue ;
142+ }
143+
144+ if (! $ file ->isReadable ()) {
145+ echo "[W] File ' " . $ file ->getFilename () . "' isn't readable. Ignoring. \n" ;
146+ continue ;
147+ }
148+
149+ /* Check if it's a HTML file, and if so, save it as one of the general
150+ documentation files to be inserted later. */
151+ if ($ file ->getExtension () == 'html ' ) {
152+ $ pages [$ file ->getFilename ()] = file_get_contents ($ file ->getPathname ());
153+
154+ continue ;
155+ }
156+
157+ /* Otherwise, we're assuming it's a PHP file with DocGen strings, and we use
158+ our custom parsing functions. */
159+ $ content = $ this ->parse_clean (file_get_contents ($ file ->getPathname ()));
160+ $ blocks = $ this ->parse_blocks ($ content );
161+
162+ $ doc_files [] = $ this ->generate_tree ($ blocks , $ file ->getFilename (), basename ($ dir ));
163+ }
164+ }
165+
166+ /* Generate routes as part of the documentation. */
167+ $ routes = [];
168+ foreach ($ doc_files as $ file ) {
169+ foreach ($ file ->getClass ()->getMethods () as $ method ) {
170+ if (count ($ method ->routes ) > 0 ) {
171+ foreach ($ method ->routes as $ route ) {
172+ $ routes [$ route [0 ]][] = [$ route [1 ], strtolower ($ file ->getClass ()->name ), $ method ->name ];
173+ }
174+ }
175+ }
176+ }
177+
178+ if ($ this ->routes_contain_duplicates ($ routes )) {
179+ echo "[E] Duplicate routes found. Quitting. \n" ;
180+ return false ;
181+ }
182+
183+ echo "Generating navigation tree for HTML output. \n" ;
184+ $ nav_tree = [];
185+ foreach ($ pages as $ index => $ page ) {
186+ $ nav_tree ['pages ' ][] = explode (". " , $ index )[0 ];
187+ }
188+ foreach ($ doc_files as $ doc_file ) {
189+ $ nav_tree [$ doc_file ->getClass ()->package ][] =
190+ ($ doc_file ->getClass ()->name != null ) ? $ doc_file ->getClass ()->name : $ doc_file ->name ;
191+ }
192+ foreach ($ nav_tree as &$ tree ) {
193+ sort ($ tree );
194+ }
195+
196+ echo "Outputting documentation as HTML. \n" ;
197+ foreach ($ pages as $ index => $ page ) {
198+ $ doc_file_path = $ targetDir . "/pages. " . $ index ;
199+ file_put_contents ($ doc_file_path , $ this ->html_page ($ page , $ nav_tree ));
200+ }
201+ foreach ($ doc_files as $ doc_file ) {
202+ $ doc_file_path = $ targetDir . "/ " . $ doc_file ->getClass ()->package . ". "
203+ . (($ doc_file ->getClass ()->name != null ) ? $ doc_file ->getClass ()->name : $ doc_file ->name )
204+ . ".html " ;
205+ file_put_contents ($ doc_file_path , $ this ->html_file ($ doc_file , $ nav_tree ));
206+ }
207+
208+ echo "Outputting route graph HTML. \n" ;
209+ file_put_contents ($ targetDir . "/routes.html " , $ this ->html_routes ($ this ->trim_toplevel_routes ($ this ->routes_by_endpoint ($ routes )), $ nav_tree ));
210+ file_put_contents ($ targetDir . "/index.html " , $ this ->html_index ($ nav_tree ));
211+ echo "Successfully generated documentation HTML. \n\n" ;
212+
213+ /* Copy style files and other general data needed for the documentation
214+ to function. */
215+ echo "Copying style and script files over to target directory. \n" ;
216+ mkdir ($ targetDir . "/style " );
217+ foreach (new \FilesystemIterator (OB_LOCAL . '/core/data/routes/style/ ' ) as $ style ) {
218+ if ($ style ->isDir ()) {
219+ continue ;
220+ }
221+
222+ if (! $ style ->isReadable ()) {
223+ echo "[W] Stylesheet ' " . $ style ->getFilename () . "' isn't readable. Ignoring. \n" ;
224+ continue ;
225+ }
226+
227+ copy ($ style ->getPathname (), $ targetDir . "/style/ " . $ style ->getFilename ());
228+ }
229+
230+ mkdir ($ targetDir . "/js " );
231+ foreach (new \FilesystemIterator (OB_LOCAL . '/core/data/routes/js/ ' ) as $ script ) {
232+ if ($ script ->isDir ()) {
233+ continue ;
234+ }
235+
236+ if (! $ script ->isReadable ()) {
237+ echo "[W] Javascript file ' " . $ script ->getFilename () . "' isn't readable. Ignoring. \n" ;
238+ continue ;
239+ }
240+
241+ copy ($ script ->getPathname (), $ targetDir . "/js/ " . $ script ->getFilename ());
242+ }
243+ echo "Successfully copied style and script files. \n\n" ;
244+
245+ echo "Successfully generated documentation. Statistics: \n" ;
246+ echo "Packages: \t" . count ($ nav_tree ) . "\n" ;
247+ echo "Classes: \t" . count ($ doc_files ) . "\n" ;
248+ echo "CSS Files: \t" . (count (scandir ($ targetDir . "/style/ " )) - 2 ) . "\n" ;
249+ echo "JS Files: \t" . (count (scandir ($ targetDir . "/js/ " )) - 2 ) . "\n\n" ;
250+
251+ return true ;
252+ }
253+
121254 /* Parsing functions. The first thing we need to do is clean the content we
122255 get from PHP files a bit. This includes removing empty lines (just in case there's
123256 gaps between DocBlocks and start of class/method definitions), trimming all the
0 commit comments