aneamal/main.php

<?php

/* Copyright 2010-2024 Martin Janecke <martin@aneamal.org>
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

// declare (strict_types = 1); // only during development

namespace prlbr\aneamal;

// error_reporting (E_ALL); // only during development

// Constants
const vmain '31';
const 
CACHE_READ 1;
const 
CACHE_FEED 2;
const 
CACHE_DROP 4;

// filename
$query $_SERVER['QUERY_STRING'];
$file  __DIR__ '/..' $query;
$base  basename ($query);

header ('Content-Type: text/html; charset=UTF-8');

// Restrict further processing to readable .nml files. The file extentsion check
// prevents sensitive data such as in .htaccess or .php from becoming public.
if (!str_ends_with ($base'.nml') or !is_readable ($file)):
    
header ('HTTP/1.1 404 Not Found');
    print <<<ANT
        <!doctype html>
        <title>404 Not Found</title>
        <h1>Not Found</h1>
        <p>The requested URL was not found on this server.</p>
        ANT;
    exit;
endif;

// Aneamal file names with an initial @-symbol are for automatic inclusion in
// other pages and not made to be accessed directly, hence the 403 status. They
// are still processed normally when accessed directly for trouble shooting.
if ($forbidden $base[0] === '@'):
    
header ('HTTP/1.1 403 Forbidden');
endif;

// QUERY_HASH is used for caching here, func.php, maybe modules. Do not change.
$hash md5 ($query);
$hash[2] = '/';
define (__NAMESPACE__ '\\QUERY_HASH'$hash);

// Determine the caching mode. AneamalCache 'on' means use cache except for
// HTTP POST requests; 'forced' uses the cache independent of HTTP method; an
// integer is like 'on' with a maximum usability period given in seconds.
$mode 0;
if (
$rule getenv ('AneamalCache')):
    
$rule strtolower ($rule);
    
$cache __DIR__ '/private/cache/' QUERY_HASH '.html';
    if (
$_SERVER['REQUEST_METHOD'] === 'POST' and $rule !== 'forced'):
        
file_exists ($cache) and $mode CACHE_DROP;
    elseif (
$life = (int) $rule or $rule === 'on' or $rule === 'forced'):
        if (
$time = @filemtime ($cache)): // checks existence and time
            
$mode $life && time () > $time $life || filemtime ($file) >= $timeCACHE_FEEDCACHE_READ;
        else:
            
$mode CACHE_FEED;
        endif;
    endif;
endif;

// Get and output the page from the cache.
if ($mode === CACHE_READ):

    
// Implements https://tools.ietf.org/html/rfc7232#section-2.2
    
header ('Last-Modified: ' gmdate ('D, d M Y H:i:s'$time) . ' GMT');

    
// Implements https://tools.ietf.org/html/rfc7232#section-3.3
    
if (
        isset (
$_SERVER['HTTP_IF_MODIFIED_SINCE'])
        and 
$_SERVER['REQUEST_METHOD'] === 'GET' || $_SERVER['REQUEST_METHOD'] === 'HEAD'
        
and @strtotime ($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $time
        
and !$forbidden
    
):
        
header ('HTTP/1.1 304 Not Modified');
        exit;
    elseif (@
readfile ($cache)):
        exit;
    endif;
endif;

// start timing of page generation
$start microtime (true);

// include the class file which does the Aneamal-HTML-translation
require __DIR__ '/html.php';

// get the Aneamal root directory relative to the document root; the 17
// removed characters from the SCRIPT_NAME are "/aneamal/main.php"
$home getenv ('AneamalHome') ?: substr ($_SERVER['SCRIPT_NAME'], 0, -17);

// create an object doing the Aneamal-HTML-translation and pass the
// Aneamal file contents to it
$translator = new nml2html (file_get_contents ($file), dirname ($query), $home$base);
$html $translator->document ();

// stop timing
$duration microtime (true) - $start;

// print the HTML page
print $html '<!--' strval ($duration) . '-->';

// write the file to the cache, create directory if necessary
if ($mode === CACHE_FEED):
    if (@
file_put_contents ($cache$html '<!--' QUERY_HASH '-from-cache-->')):
        print 
'<!--written-to-cache-->';
    elseif (
is_dir ($dir dirname ($cache)) or !@mkdir ($dir0777true)):
        print 
'<!--failed-to-cache-->';
    elseif (@
file_put_contents ($cache$html '<!--' QUERY_HASH '-from-cache-->')):
        print 
'<!--written-to-cache-->';
    else:
        print 
'<!--failed-to-cache-->';
    endif;
elseif (
$mode === CACHE_DROP):
    
touch ($cache0);
endif;

// The End