Skip to content

Instantly share code, notes, and snippets.

@frumbert
Created April 13, 2020 04:01
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save frumbert/f91133e5ccf9b48287045e9390934528 to your computer and use it in GitHub Desktop.
Sort and Link to markdown files in nested sub-folders in PHP (e.g. build documentation)
<?php
/*
You have markdown files in nested folders which comtain
--01.getting-started
| |
| +--02.finalisation
| | |
| | docs.md
| | picture.png
| |
| +--01.installation
| |
| docs.md
| readme.txt
| media.jpg
|
|--02.profit
| |
| +--01.set-up
| |
| docs.md
|
+--intro.md
You want
<ol>
<li><a href='intro.md'>Getting Started</a>
<ol>
<li><a href='01.getting-started/01.installation/docs.md'>Getting Started</a></li>
<li><a href='01.getting-started/02.finalisation/docs.md'>Finalisation</a></li>
</ol>
<li>Profit
<ol>
<li><a href='02.profit/01.set-up/docs.md'>Set Up</a></li>
</ol>
</li>
</ol>
This script will do that efficiently using recursive directory iterators, regular expression iterators, sorting iterators, and array manipulation
Not shown here: Caching
*/
$documentation_root = '.';
// sort each item in an iterator by name
class ExampleSortedIterator extends SplHeap
{
public function __construct(Iterator $iterator)
{
foreach ($iterator as $item) {
$this->insert($item);
}
}
public function compare($b,$a)
{
return strcmp($a->getRealpath(), $b->getRealpath());
}
}
// list all files recursively, skipping dotfiles, and sort each directory by name
$rii = new ExampleSortedIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($documentation_root, FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS)));
// set up output array
$files = array();
// go through each sorted file
foreach ($rii as $file) {
// skip non-markdown files
if (strpos($file->getFilename(), ".md") === false) continue;
$name = substr($file->getPathname(), 2);
// turn 01.folder-name/01.sub-folder-name/docs.md into [01.folder-name, 01.sub-folder-name, docs.md]
$parts = explode('/',$name);
// create a reference to the output array
$current = &$files;
foreach ($parts as $key) {
// the reference is itself a reference with the key set as the step of the array
// each item in parts becomes an array with that name as a key
$current = &$current[$key];
}
// $rii is now collapsed into a nested sorted array with null-terminated leaf nodes
}
function render($ol, $path_to_here = '.') {
$result = [];
foreach ($ol as $key => $value) {
// if the value is null then we are somehow inside a leaf node (could throw exception)
if (is_null($value)) continue;
// turn "02.some-label-value" into "Some Label Value"
$label = ucwords(str_replace('-',' ', preg_replace('/^\d{1,}\./', '',$key)));
// look in this value to see if there's a leaf node
// link to the leaf node, or just render the label
$link = '';
foreach ($value as $branch => $leaf) {
if (is_null($leaf)) {
$link = $branch;
break;
}
}
$result[] = "<li>";
if (empty($link)) {
$result[] = $label;
} else {
$result[] = "<a href='?url={$path_to_here}/{$key}/{$link}'>{$label}</a>";
}
$children = render($value, $path_to_here . '/' . $key);
if (!empty($children)) {
$result[] = "<ol>";
$result = array_merge($result, $children);
$result[] = "</ol>";
}
$result[] = "</li>" . PHP_EOL;
}
return $result;
}
$output = render($files);
echo "<ol>", PHP_EOL;
echo implode('', $output);
echo "</ol>", PHP_EOL;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment