This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php namespace Myth\Docs; |
||
2 | /** |
||
3 | * Sprint |
||
4 | * |
||
5 | * A set of power tools to enhance the CodeIgniter framework and provide consistent workflow. |
||
6 | * |
||
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
||
8 | * of this software and associated documentation files (the "Software"), to deal |
||
9 | * in the Software without restriction, including without limitation the rights |
||
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||
11 | * copies of the Software, and to permit persons to whom the Software is |
||
12 | * furnished to do so, subject to the following conditions: |
||
13 | * |
||
14 | * The above copyright notice and this permission notice shall be included in |
||
15 | * all copies or substantial portions of the Software. |
||
16 | * |
||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||
23 | * THE SOFTWARE. |
||
24 | * |
||
25 | * @package Sprint |
||
26 | * @author Lonnie Ezell |
||
27 | * @copyright Copyright 2014-2015, New Myth Media, LLC (http://newmythmedia.com) |
||
28 | * @license http://opensource.org/licenses/MIT (MIT) |
||
29 | * @link http://sprintphp.com |
||
30 | * @since Version 1.0 |
||
31 | */ |
||
32 | |||
33 | use Myth\Docs\DocBuilderInterface; |
||
34 | |||
35 | /** |
||
36 | * Class Builder |
||
37 | * |
||
38 | * Handles the brunt of building documentation from Markdown formatted files. |
||
39 | * |
||
40 | * @package Myth\Docs |
||
41 | */ |
||
42 | class Builder implements DocBuilderInterface |
||
43 | { |
||
44 | |||
45 | protected $docs_ext = '.md'; |
||
46 | |||
47 | protected $ignore_files = ['_404.md']; |
||
48 | |||
49 | protected $doc_folders = []; |
||
50 | |||
51 | /** |
||
52 | * Stores the current folder alias, |
||
53 | * once the file has been found. |
||
54 | * |
||
55 | * @var null |
||
56 | */ |
||
57 | protected $current_folder = null; |
||
58 | |||
59 | protected $table_classes = 'table table-hover'; |
||
60 | |||
61 | protected $apppath = ''; |
||
62 | |||
63 | protected $formatters = []; |
||
64 | |||
65 | protected $page_title = null; |
||
66 | |||
67 | //-------------------------------------------------------------------- |
||
68 | |||
69 | public function __construct($config = array()) |
||
70 | { |
||
71 | $this->apppath = ! empty($config['apppath']) ? rtrim($config['apppath'], '/') . '/' : ''; |
||
72 | } |
||
73 | |||
74 | //-------------------------------------------------------------------- |
||
75 | |||
76 | public function pageTitle() |
||
77 | { |
||
78 | return $this->page_title; |
||
79 | } |
||
80 | |||
81 | //-------------------------------------------------------------------- |
||
82 | |||
83 | |||
84 | |||
85 | /** |
||
86 | * Does the actual work of reading in and parsing the help file. |
||
87 | * If a folder Nickname (see addDocFolder() ) is passed as the second parameter, |
||
88 | * it will limit it's search to that single folder. If nothing is passed, it will |
||
89 | * search through all of the folders in the order they were given to the library, |
||
90 | * until it finds the first one. |
||
91 | * |
||
92 | * @param string $path The 'path' of the file (relative to the docs |
||
93 | * folder. Usually from the URI) |
||
94 | * @param string $restrictToFolder (Optional) The folder nickname |
||
95 | * |
||
96 | * @return string |
||
97 | */ |
||
98 | public function readPage($path, $restrictToFolder = null) |
||
99 | { |
||
100 | // Clean up our path |
||
101 | $path = trim($path, '/ '); |
||
102 | |||
103 | $content = $this->locateAndReadFile($path, $restrictToFolder); |
||
104 | |||
105 | $content = $this->parse($content); |
||
106 | |||
107 | return $content; |
||
108 | } |
||
109 | |||
110 | //-------------------------------------------------------------------- |
||
111 | |||
112 | /** |
||
113 | * Parses the contents. Currently runs through the Markdown Extended |
||
114 | * parser to convert to HTML. |
||
115 | * |
||
116 | * @param $str |
||
117 | * @return mixed |
||
118 | */ |
||
119 | public function parse($str) |
||
120 | { |
||
121 | return $this->format($str); |
||
122 | } |
||
123 | |||
124 | //-------------------------------------------------------------------- |
||
125 | |||
126 | /** |
||
127 | * Perform a few housekeeping tasks on a page, like rewriting URLs to full |
||
128 | * URLs, not relative, ensuring they link correctly, etc. |
||
129 | * |
||
130 | * @param $content |
||
131 | * @param null $site_url |
||
132 | * @param null $current_url |
||
133 | * @return string The post-processed HTML. |
||
134 | */ |
||
135 | public function postProcess($content, $site_url = null, $current_url = null) |
||
136 | { |
||
137 | if (empty($content)) { |
||
138 | return $content; |
||
139 | } |
||
140 | |||
141 | try { |
||
142 | $xml = new \SimpleXMLElement('<?xml version="1.0" standalone="yes"?><div>' . $content . '</div>'); |
||
143 | } catch (\Exception $e) { |
||
144 | // SimpleXML barfed on us, so send back the un-modified content |
||
145 | return $content; |
||
146 | } |
||
147 | |||
148 | // Prepare some things and cleanup others |
||
149 | $groups = array_keys($this->doc_folders); |
||
150 | $site_url = rtrim($site_url, '/') . '/'; |
||
151 | $current_url = rtrim($current_url, '#/'); |
||
152 | |||
153 | // Try to determine the current_url if one isn't set. |
||
154 | if (empty($this->current_folder)) { |
||
155 | $this->current_folder = $this->detectCurrentFolder($current_url, $groups); |
||
156 | } |
||
157 | |||
158 | /* |
||
159 | * Rewrite the URLs |
||
160 | */ |
||
161 | foreach ($xml->xpath('//a') as $link) { |
||
162 | $link = $this->reformatAnchor($link, $groups, $current_url, $site_url); |
||
163 | } |
||
164 | |||
165 | $content = $xml->asXML(); |
||
166 | $content = trim(str_replace('<?xml version="1.0" standalone="yes"?>', '', $content)); |
||
167 | |||
168 | // Clean up and style the tables |
||
169 | $content = str_replace('<table>', '<table class="' . $this->table_classes . '">', $content); |
||
170 | |||
171 | return $content; |
||
172 | } |
||
173 | //-------------------------------------------------------------------- |
||
174 | |||
175 | /** |
||
176 | * Allows users to define the classes that are attached to |
||
177 | * generated tables. |
||
178 | * |
||
179 | * @param null $classes |
||
180 | * @return $this |
||
181 | */ |
||
182 | public function setTableClasses($classes = null) |
||
183 | { |
||
184 | $this->table_classes = $classes; |
||
185 | |||
186 | return $this; |
||
187 | } |
||
188 | |||
189 | //-------------------------------------------------------------------- |
||
190 | |||
191 | /** |
||
192 | * Given the contents to render, will build a list of links for the sidebar |
||
193 | * out of the headings in the file. |
||
194 | * |
||
195 | * Note: Will ONLY use h2 and h3 to build the links from. |
||
196 | * |
||
197 | * Note: The $content passed in WILL be modified by adding named anchors |
||
198 | * that match up with the locations. |
||
199 | * |
||
200 | * @param string $content The HTML to analyse for headings. |
||
201 | * @return string |
||
202 | */ |
||
203 | public function buildDocumentMap(&$content) |
||
204 | { |
||
205 | if (empty($content)) { |
||
206 | return $content; |
||
207 | } |
||
208 | |||
209 | // If $content already has a wrapping <div> and </div> tags, remove them, |
||
210 | // since we'll replace them just below. |
||
211 | if (strpos($content, '<div>') === 0) { |
||
212 | $content = substr($content, 5); |
||
213 | |||
214 | // Trailing div also? |
||
215 | if (substr($content, -6) == '</div>') { |
||
216 | $content = substr($content, 0, -6); |
||
217 | } |
||
218 | } |
||
219 | |||
220 | try { |
||
221 | $xml = new \SimpleXMLElement('<?xml version="1.0" standalone="yes"?><div>' . $content . '</div>'); |
||
222 | } catch (\Exception $e) { |
||
223 | // SimpleXML barfed on us, so send back the un-modified content |
||
224 | return []; |
||
225 | } |
||
226 | |||
227 | $map = []; |
||
228 | list($map, $content) = $this->extractDocMapAndAddAnchors($content, $xml, $map); |
||
229 | |||
230 | return $map; |
||
231 | } |
||
232 | |||
233 | //-------------------------------------------------------------------- |
||
234 | |||
235 | /** |
||
236 | * Stores the name of the callback method to run to convert the source |
||
237 | * files to viewable files. By default, this should be used to register |
||
238 | * a Mardown Extended formatter with the system, but could be used to |
||
239 | * extend the |
||
240 | * |
||
241 | * @param string $callback |
||
242 | * @param bool $cascade // If FALSE the formatting of a component ends here. If TRUE, will be passed to next formatter. |
||
243 | * @return $this |
||
244 | */ |
||
245 | public function registerFormatter($callback = null, $cascade = false) |
||
246 | { |
||
247 | if (empty($callback)) return; |
||
248 | |||
249 | $this->formatters[] = [ |
||
250 | 'callable' => $callback, |
||
251 | 'cascade' => (bool)$cascade |
||
252 | ]; |
||
253 | |||
254 | return $this; |
||
255 | } |
||
256 | |||
257 | //-------------------------------------------------------------------- |
||
258 | |||
259 | /** |
||
260 | * Runs the text through the registered formatters. |
||
261 | * |
||
262 | * @param $str |
||
263 | * @return mixed |
||
264 | */ |
||
265 | View Code Duplication | public function format($str) |
|
266 | { |
||
267 | if (! is_array($this->formatters)) return $str; |
||
268 | |||
269 | foreach ($this->formatters as $formatter) { |
||
270 | $method = $formatter['callable']; |
||
271 | $cascade = $formatter['cascade']; |
||
272 | |||
273 | $str = call_user_func($method, $str); |
||
274 | |||
275 | if (! $cascade) return $str; |
||
276 | } |
||
277 | |||
278 | return $str; |
||
279 | } |
||
280 | |||
281 | //-------------------------------------------------------------------- |
||
282 | |||
283 | //-------------------------------------------------------------------- |
||
284 | // Table of Contents methods |
||
285 | //-------------------------------------------------------------------- |
||
286 | |||
287 | /** |
||
288 | * Retrieves the list of files in a folder and preps the name and filename |
||
289 | * so it's ready for creating the HTML. |
||
290 | * |
||
291 | * @param String $folder The path to the folder to retrieve. |
||
292 | * |
||
293 | * @return Array An associative array @see parse_ini_file for format |
||
294 | * details. |
||
295 | */ |
||
296 | public function buildTOC($folder) |
||
297 | { |
||
298 | // If the toc file exists in the folder, use it to build the links. |
||
299 | if (is_file("{$folder}/_toc.ini")) { |
||
300 | $toc = parse_ini_file("{$folder}/_toc.ini", true); |
||
301 | return $this->columnizeTOC($toc); |
||
302 | } |
||
303 | |||
304 | // If the toc file does not exist, build the links by listing the files |
||
305 | // in the directory (and any sub-directories) |
||
306 | $map = $this->directory_map($folder); |
||
307 | |||
308 | // If directory_map can not open the directory or find any files inside |
||
309 | // the directory, return an empty array. |
||
310 | if (empty($map)) { |
||
311 | return []; |
||
312 | } |
||
313 | |||
314 | // If these docs are located in the /application/docs or /bonfire/docs |
||
315 | // directory, just use $this->current_group for the root. |
||
316 | // Module docs need $this->current_group and $type. |
||
317 | $tocRoot = $this->current_folder; |
||
318 | if ($this->current_folder != strtolower($folder)) { |
||
319 | $tocRoot .= '/' . strtolower($folder); |
||
320 | } |
||
321 | |||
322 | $toc = []; |
||
323 | foreach ($map as $files) { |
||
324 | // If $files isn't an array, then make it one so that all situations |
||
325 | // may be dealt with cleanly. |
||
326 | if (! is_array($files)) { |
||
327 | $files = [$files]; |
||
328 | } |
||
329 | |||
330 | foreach ($files as $file) { |
||
331 | if (in_array($file, $this->ignore_files)) { |
||
332 | continue; |
||
333 | } |
||
334 | |||
335 | // The title for the index is the passed $type. Otherwise, |
||
336 | // build the title from the file's name. |
||
337 | if (strpos($file, 'index') === false) { |
||
338 | $title = str_replace($this->docs_ext, '', $file); |
||
339 | $title = str_replace('_', ' ', $title); |
||
340 | $title = ucwords($title); |
||
341 | |||
342 | $toc["{$tocRoot}/{$file}"] = $title; |
||
343 | } else { |
||
344 | $toc[$tocRoot] = $type; |
||
345 | } |
||
346 | } |
||
347 | } |
||
348 | |||
349 | $toc = $this->columnizeTOC($toc); |
||
350 | |||
351 | return $toc; |
||
352 | } |
||
353 | |||
354 | //-------------------------------------------------------------------- |
||
355 | |||
356 | /** |
||
357 | * Sorts the passed TOC array into columns of as close to equal length |
||
358 | * as we can get it. |
||
359 | * |
||
360 | * @param $toc |
||
361 | * @return array |
||
362 | */ |
||
363 | protected function columnizeTOC($toc) |
||
364 | { |
||
365 | $section_count = count($toc); |
||
366 | |||
367 | // First - determine the size of each 'section'. |
||
368 | $sizes = []; |
||
369 | |||
370 | foreach ($toc as $section => $chapters) { |
||
371 | $sizes[] = count($chapters); |
||
372 | } |
||
373 | |||
374 | $column_avg = (int)round(array_sum($sizes) / $section_count); |
||
375 | |||
376 | // Split things into 4 columns of approximately equal size. |
||
377 | // If we only have 4 columns (or less), then make sure to |
||
378 | // deal with that also. |
||
379 | $columns = []; |
||
380 | |||
381 | $current_column = 0; |
||
382 | $current_column_count = 0; |
||
383 | $keys = array_keys($toc); |
||
384 | |||
385 | for ($i = 0; $i <= $section_count; $i++) { |
||
386 | if (! isset($keys[$i])) { |
||
387 | continue; |
||
388 | } |
||
389 | |||
390 | $section = array_shift($toc); |
||
391 | |||
392 | // Can we stay in this column? |
||
393 | if ($current_column_count <= $column_avg && $section_count > 4) { |
||
394 | // Don't forget to account for the heading also. |
||
395 | $current_column_count += count($section) + 1; |
||
396 | } else { |
||
397 | $current_column_count = 0; |
||
398 | $current_column++; |
||
399 | } |
||
400 | |||
401 | $columns[$current_column][$keys[$i]] = $section; |
||
402 | } |
||
403 | |||
404 | return $columns; |
||
405 | } |
||
406 | |||
407 | //-------------------------------------------------------------------- |
||
408 | |||
409 | //-------------------------------------------------------------------- |
||
410 | // Folder Methods |
||
411 | //-------------------------------------------------------------------- |
||
412 | |||
413 | /** |
||
414 | * Returns the current docFolders array. |
||
415 | * |
||
416 | * @return array |
||
417 | */ |
||
418 | public function docFolders() |
||
419 | { |
||
420 | return $this->doc_folders; |
||
421 | } |
||
422 | |||
423 | //-------------------------------------------------------------------- |
||
424 | |||
425 | /** |
||
426 | * Registers a path to be used when searching for documentation files. |
||
427 | * |
||
428 | * @param $name A nickname to reference it by later. |
||
429 | * @param $path The server path to the folder. |
||
430 | * @return $this |
||
431 | */ |
||
432 | public function addDocFolder($name, $path) |
||
433 | { |
||
434 | // Standardize the path |
||
435 | $path = realpath($path) . '/'; |
||
436 | |||
437 | // realpath will return FALSE if the path doesn't exist |
||
438 | // or the script doesn't have access to it. |
||
439 | if (! $path || $path == '/') { |
||
440 | return $this; |
||
441 | } |
||
442 | |||
443 | $name = strtolower($name); |
||
444 | |||
445 | $this->doc_folders[$name] = $path; |
||
446 | |||
447 | return $this; |
||
448 | } |
||
449 | |||
450 | //-------------------------------------------------------------------- |
||
451 | |||
452 | /** |
||
453 | * Removes a folder from the folders we scan for documentation files |
||
454 | * within. |
||
455 | * |
||
456 | * @param $name |
||
457 | * @return $this |
||
458 | */ |
||
459 | public function removeDocFolder($name) |
||
460 | { |
||
461 | $name = strtolower($name); |
||
462 | |||
463 | if (isset($this->doc_folders[$name])) { |
||
464 | unset($this->doc_folders[$name]); |
||
465 | } |
||
466 | |||
467 | return $this; |
||
468 | } |
||
469 | |||
470 | //-------------------------------------------------------------------- |
||
471 | |||
472 | //-------------------------------------------------------------------- |
||
473 | // Private Methods |
||
474 | //-------------------------------------------------------------------- |
||
475 | |||
476 | /** |
||
477 | * Analyzes the passed in current url string and checks against |
||
478 | * a list of groups to determine what the current group is. |
||
479 | * |
||
480 | * @param $current_url |
||
481 | * @param $groups |
||
482 | * @return string |
||
483 | */ |
||
484 | protected function detectCurrentFolder($current_url, $groups = []) |
||
485 | { |
||
486 | if (! is_array($groups)) { |
||
487 | return null; |
||
488 | } |
||
489 | |||
490 | $segments = explode('/', $current_url); |
||
491 | |||
492 | // We start from the back of the array since |
||
493 | // that's most likely to be close to the end. |
||
494 | $segments = array_reverse($segments); |
||
495 | |||
496 | foreach ($segments as $segment) { |
||
497 | foreach ($groups as $group) { |
||
498 | if (strtolower($group) == strtolower($segment)) { |
||
499 | return $group; |
||
500 | } |
||
501 | } |
||
502 | } |
||
503 | |||
504 | // Nothing found? |
||
505 | return null; |
||
506 | } |
||
507 | |||
508 | //-------------------------------------------------------------------- |
||
509 | |||
510 | //-------------------------------------------------------------------- |
||
511 | // Private Methods |
||
512 | //-------------------------------------------------------------------- |
||
513 | |||
514 | /** |
||
515 | * Locates the file on disk and reads the contents into a single string. |
||
516 | * |
||
517 | * If a folder Nickname (see addDocFolder() ) is passed as the second parameter, |
||
518 | * it will limit it's search to that single folder. If nothing is passed, it will |
||
519 | * search through all of the folders in the order they were given to the library, |
||
520 | * until it finds the first one. |
||
521 | * |
||
522 | * @param string $path The 'path' of the file (relative to the docs |
||
523 | * folder. Usually from the URI) |
||
524 | * @param string $restrictToFolder (Optional) The nickname of one of the |
||
525 | * folders to restrict the search to. |
||
526 | * |
||
527 | * @throws RuntimeException |
||
528 | * @return null|string |
||
529 | */ |
||
530 | private function locateAndReadFile($path, $restrictToFolder = null) |
||
531 | { |
||
532 | $folders = $this->doc_folders; |
||
533 | |||
534 | if (! is_null($restrictToFolder)) { |
||
535 | // Make sure the folder exists |
||
536 | if (! is_null($restrictToFolder) && ! isset($this->doc_folders[$restrictToFolder])) { |
||
537 | throw new \RuntimeException('You must add the docs folder that you wish to find docs from.'); |
||
538 | } |
||
539 | |||
540 | $folders = [$this->doc_folders[$restrictToFolder]]; |
||
541 | } |
||
542 | |||
543 | foreach ($folders as $alias => $folder) { |
||
544 | if (file_exists($folder . $path . $this->docs_ext)) { |
||
545 | // Store the alias so we know which folder we're in. |
||
546 | $this->current_folder = $alias; |
||
547 | |||
548 | return file_get_contents($folder . $path . $this->docs_ext); |
||
549 | } |
||
550 | } |
||
551 | |||
552 | return null; |
||
553 | } |
||
554 | |||
555 | //-------------------------------------------------------------------- |
||
556 | |||
557 | /** |
||
558 | * Re-formats the passed in link. |
||
559 | * |
||
560 | * @param $link |
||
561 | * @param $current_url |
||
562 | * @param $site_url |
||
563 | * @return mixed |
||
564 | */ |
||
565 | private function reformatAnchor($link, $groups, $current_url, $site_url) |
||
566 | { |
||
567 | // Grab the href value. |
||
568 | $href = $link->attributes()->href; |
||
569 | |||
570 | // If the href is null, it's probably a named anchor with no content. |
||
571 | if (! $href) { |
||
572 | // Make sure it has an href, else the XML will not close this |
||
573 | // tag correctly. |
||
574 | $link['href'] = ' '; |
||
575 | |||
576 | return $link; |
||
577 | } |
||
578 | |||
579 | // Remove any trailing # signs |
||
580 | $href = rtrim($href, '# '); |
||
581 | |||
582 | // If the href starts with #, then attach the current_url to it |
||
583 | if ($href != '' && substr_compare($href, '#', 0, 1) === 0) { |
||
584 | $link['href'] = $current_url . $href; |
||
585 | |||
586 | return $link; |
||
587 | } |
||
588 | |||
589 | // If it's a full external path, go on... |
||
590 | if ((strpos($href, 'http://') !== false || strpos($href, 'https://') !== false) && |
||
591 | strpos($href, $site_url) === false |
||
592 | ) { |
||
593 | $link['target'] = "_blank"; |
||
594 | return $link; |
||
595 | } |
||
596 | |||
597 | // If it's a full local path, get rid of it. |
||
598 | if ($site_url !== "/" && strpos($href, $site_url) !== false) { |
||
599 | $href = str_replace($site_url, '', $href); |
||
600 | } |
||
601 | |||
602 | // Strip out some unnecessary items, just in case they're there. |
||
603 | if (substr($href, 0, strlen('docs/')) == 'docs/') { |
||
604 | $href = substr($href, strlen('docs/')); |
||
605 | } |
||
606 | |||
607 | // This includes 'bonfire/' if it was missed during the conversion. |
||
608 | if (substr($href, 0, strlen('bonfire/')) == 'bonfire/') { |
||
609 | $href = substr($href, strlen('bonfire/')); |
||
610 | } |
||
611 | |||
612 | // If another 'group' is not already defined at the head of the link |
||
613 | // then add the current group to it. |
||
614 | $group_found = false; |
||
615 | |||
616 | foreach ($groups as $group) { |
||
617 | if (strpos($href, $group) === 0) { |
||
618 | $group_found = true; |
||
619 | } |
||
620 | } |
||
621 | |||
622 | if (! $group_found) { |
||
623 | $href = $this->current_folder . '/' . $href; |
||
624 | } |
||
625 | |||
626 | // Convert to full site_url |
||
627 | if (strpos($href, 'http') !== 0) { |
||
628 | $href = $site_url . 'docs/' . ltrim($href, '/ '); |
||
629 | } |
||
630 | |||
631 | // Save the corrected href |
||
632 | $link['href'] = $href; |
||
633 | |||
634 | return $link; |
||
635 | } |
||
636 | |||
637 | //-------------------------------------------------------------------- |
||
638 | |||
639 | /** |
||
640 | * Creates a Document Map based on <h2> and <h3> tags. |
||
641 | * Also adds named anchors into the $content so the map |
||
642 | * can link to the content properly. |
||
643 | * |
||
644 | * @param $content |
||
645 | * @param $xml |
||
646 | * @param $map |
||
647 | * @return array |
||
648 | */ |
||
649 | protected function extractDocMapAndAddAnchors(&$content, $xml, $map) |
||
650 | { |
||
651 | // Holds the current h2 we're processing |
||
652 | $current_obj = []; |
||
653 | |||
654 | $currentChild = 0; |
||
655 | |||
656 | foreach ($xml->children() as $childType => $line) { |
||
657 | $currentChild++; |
||
658 | |||
659 | // If it's an h1 - take the first and make it |
||
660 | // our page title. |
||
661 | if ($childType == 'h1' && empty($this->page_title)) |
||
662 | { |
||
663 | $this->page_title = (string)$line; |
||
664 | } |
||
665 | |||
666 | // Make sure that our current object is |
||
667 | // stored and reset. |
||
668 | if ($childType == 'h1' || $childType == 'h2') { |
||
669 | if (count($current_obj)) { |
||
670 | $map[] = $current_obj; |
||
671 | $current_obj = []; |
||
672 | } |
||
673 | } |
||
674 | |||
675 | if ($childType == 'h2') { |
||
676 | $name = (string)$line; |
||
677 | $link = strtolower(str_replace(' ', '_', (string)$line)); |
||
678 | |||
679 | $current_obj['name'] = $name; |
||
680 | $current_obj['link'] = '#' . $link; |
||
681 | $current_obj['items'] = []; |
||
682 | |||
683 | // Insert a named anchor into the $content |
||
684 | $anchor = '<a name="' . $link . '" id="' . $link . '" ></a>'; |
||
685 | |||
686 | $search = "<h2>{$name}</h2>"; |
||
687 | |||
688 | $content = str_replace($search, $anchor . $search, $content); |
||
689 | } elseif ($childType == 'h3') { |
||
690 | // Make sure we have some place to store the items. |
||
691 | if (! isset($current_obj['items'])) { |
||
692 | $current_obj['items'] = []; |
||
693 | } |
||
694 | |||
695 | $link = strtolower(str_replace(' ', '_', (string)$line)); |
||
696 | $name = (string)$line; |
||
697 | |||
698 | $current_obj['items'][] = [ |
||
699 | 'name' => $name, |
||
700 | 'link' => '#' . $link |
||
701 | ]; |
||
702 | |||
703 | // Insert a named anchor into the $content |
||
704 | $anchor = '<a name="' . $link . '" id="' . $link . '" ></a>'; |
||
705 | |||
706 | $search = "<h3>{$name}</h3>"; |
||
707 | |||
708 | $content = str_replace($search, $anchor . $search, $content); |
||
709 | } |
||
710 | |||
711 | // Is this the last element? Then close out our current object. |
||
712 | if (count($xml) == $currentChild) { |
||
713 | if (count($current_obj)) { |
||
714 | $map[] = $current_obj; |
||
715 | } |
||
716 | } |
||
717 | } |
||
718 | return [$map, $content]; |
||
719 | } |
||
720 | //-------------------------------------------------------------------- |
||
721 | |||
722 | /** |
||
723 | * Create a Directory Map |
||
724 | * |
||
725 | * Reads the specified directory and builds an array |
||
726 | * representation of it. Sub-folders contained with the |
||
727 | * directory will be mapped as well. |
||
728 | * |
||
729 | * @param string $source_dir Path to source |
||
730 | * @param int $directory_depth Depth of directories to traverse |
||
731 | * (0 = fully recursive, 1 = current dir, etc) |
||
732 | * @param bool $hidden Whether to show hidden files |
||
733 | * @return array |
||
734 | */ |
||
735 | View Code Duplication | protected function directory_map($source_dir, $directory_depth = 0, $hidden = FALSE) |
|
736 | { |
||
737 | if ($fp = @opendir($source_dir)) { |
||
738 | $filedata = array(); |
||
739 | $new_depth = $directory_depth - 1; |
||
740 | $source_dir = rtrim($source_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
||
741 | |||
742 | while (FALSE !== ($file = readdir($fp))) { |
||
743 | // Remove '.', '..', and hidden files [optional] |
||
0 ignored issues
–
show
|
|||
744 | if ($file === '.' OR $file === '..' OR ($hidden === FALSE && $file[0] === '.')) { |
||
745 | continue; |
||
746 | } |
||
747 | |||
748 | is_dir($source_dir . $file) && $file .= DIRECTORY_SEPARATOR; |
||
749 | |||
750 | if (($directory_depth < 1 OR $new_depth > 0) && is_dir($source_dir . $file)) { |
||
751 | $filedata[$file] = directory_map($source_dir . $file, $new_depth, $hidden); |
||
752 | } else { |
||
753 | $filedata[] = $file; |
||
754 | } |
||
755 | } |
||
756 | |||
757 | closedir($fp); |
||
758 | return $filedata; |
||
759 | } |
||
760 | |||
761 | return FALSE; |
||
762 | } |
||
763 | |||
764 | //-------------------------------------------------------------------- |
||
765 | |||
766 | } |
||
767 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.