Source Code for /public/appendix-quine/index.php

<?php
// Include the functions.php file, which provides functions we use all over the place:
require('../inc/functions.php');

// Render our HTML header, including this lesson's title
add_header('Appendix C: Source Code for <em>This Site</em>');
?>

<p>
  The source code for this site <a href="https://github.com/dan-q/php-cookery-class">is available on GitHub</a>,
  but you can also browse it right here.
</p>

<ul class="source-tree">
  <?php
  // Renders a link to a file, for use by render_tree()
  function render_file_link( $file ) {
    $app_root = realpath( __DIR__ . '/../../' ) . '/';
    $file = str_replace( $app_root, '', realpath( $file ) );
    echo '<li><a href="/source-viewer.php?file=' . $file . '">' . basename( $file ) . '</a></li>';
  }

  // Sorts the files in a given path using our own mechanism:
  // Directories first, starting with public, then index.php if present, then everything else alphabetically
  function sort_files( $files ) {
    $public_dir = null;
    $directories = [];
    $index_file = null;
    $other_files = [];

    // Separate public directory, other directories, index.php, then other files
    foreach( $files as $file ) {
      if( is_dir( $file ) && ( basename( $file ) === 'public' ) ) {
        $public_dir = $file;
      } elseif( is_dir( $file ) ) {
        $directories[] = $file;
      } elseif( basename( $file ) === 'index.php' ) {
        $index_file = $file;
      } else {
        $other_files[] = $file;
      }
    }

    // Sort directories alphabetically
    sort( $directories );
    // Sort other files alphabetically
    sort( $other_files );

    // Combine: public directory if present, then directories, then index.php if present, then other files
    $sorted = [];
    if( $public_dir !== null ) $sorted[] = $public_dir;
    $sorted = array_merge( $sorted, $directories );
    if( $index_file !== null ) $sorted[] = $index_file;
    $sorted = array_merge( $sorted, $other_files );

    return $sorted;
  }

  // Renders a tree of files and directories at a given path
  function render_tree( $path ) {
    // Get a list of all the files and directories in the current path
    $files = sort_files( glob($path . '/*') );

    // List of files and directories to exclude
    $files_to_exclude = [
      realpath( __DIR__ . '/../../vendor' ),
    ];

    // Loop through each file and directory
    foreach( $files as $file ) {

      // If the file or directory is in the list of files to exclude, skip it
      if( in_array( realpath( $file ), $files_to_exclude ) ) continue;

      // Get the short (unpathed) name of the file or directory
      $file_name = basename( $file );

      if( is_dir( $file ) ) {
        // If the file is a directory, recursively render a tree of its contents
        echo '<li>' . $file_name . '<ul>';
        render_tree( $file );
        echo '</ul></li>';
      } else {
        // Otherwise, render a source viewer link to that file
        render_file_link( $file );
      }
    }
  }

  render_tree( '../../' );
  ?>
</ul>

<?php
// Render our HTML footer
add_footer();
?>