- Add test folder
[xhtml-compiler.git] / functions.php
blob9ff996e2cda9cc3d119874c96772f6e02f439706
1 <?php
3 /**
4 * Convenience function that outputs an error and halts execution
5 * @param $code integer HTTP error code
6 * @param $text Error name string, leave as false to use default
7 * @param $details Optional details string explaining error
8 * @todo Valid markup, user-defined custom error-pages
9 */
10 function display_error_and_quit($code, $text = false, $details = false) {
11 $xc = XHTMLCompiler::getInstance();
12 $default = set_response_code($code);
13 if (!$text) $text = $default;
14 $out = "<h1>Error: $text</h1>";
15 if ($details) $out .= $details;
16 $xc->paint($out);
17 $xc->quit(); // essential, will exit
20 /**
21 * Convenience function that outputs an HTTP status code
22 * @param $code integer HTTP status code
23 * @return string HTTP status code's name
25 function set_response_code($code) {
26 $xc = XHTMLCompiler::getInstance();
27 $code_descriptions = array( // could be factored out
28 200 => 'Okay',
29 304 => 'Not Modified',
30 401 => 'Unauthorized',
31 403 => 'Forbidden',
32 404 => 'Not Found',
33 503 => 'Service Unavailable',
35 $code = (int) $code; // enforce integer
36 $text = (string) $code;
37 if (isset($code_descriptions[$code])) {
38 $text .= ' ' . $code_descriptions[$code];
40 $xc->header($_SERVER['SERVER_PROTOCOL'] . ' ' . $text, true, $code);
41 $xc->header('Status: ' . $text);
42 return $text;
45 /**
46 * Determines the requested page from server environment files
47 * @return string page name
49 function get_page_from_server() {
50 $xc = XHTMLCompiler::getInstance();
51 // load up page from environment variables, if using ErrorDocument impl.
52 $page = $xc->getRequestURI();
53 $self = dirname($xc->getPHPSelf());
54 $root = strlen(substr($self, 0, strrpos($self, '/')));
55 $page = substr($page, $root + 1); // remove leading slash and root
56 return $page;
59 /**
60 * Determines the requested page from get parameter
61 * @return string page name
63 function get_page_from_get() {
64 $page == false;
65 if (isset($_GET['f'])) $page = $_GET['f'];
66 return $page;
69 /**
70 * Takes a page name and appends index filename if necessary
71 * @param $page string page name
72 * @param $directory_index string name of file to use as directory index
74 function normalize_index($page, $directory_index) {
75 if ($page == '') $page = $directory_index;
76 if (is_dir($page)) {
77 if ($page[strlen($page)-1] !== '/') $page .= '/';
78 $page .= $directory_index;
80 return $page;
83 /**
84 * Determines page and page source based on a filename
85 * @param $page_or_src filename of actual page or source file
86 * @return array(page filename, source filename)
88 function calculate_page_and_src($page_or_src) {
89 $root = substr($page_or_src, 0, strrpos($page_or_src, '.'));
90 return array($root . '.html', $root . '.xhtml');
93 /**
94 * Validates a page name, making sure that is allowed and in proper format;
95 * halts page execution if invalid
96 * @param $page string page name to validate
97 * @param $allowed_dirs lookup array of allowed directories, see config.default.php
98 * @param $filename_chars PCRE style string of characters, see config.default.php
100 function validate_page($page, $allowed_dirs, $filename_chars) {
101 // validate the path, perhaps syntax could be more permissive
102 $regex = "#(((?:[$filename_chars]+/)*)[$filename_chars]+).html#";
103 $status = preg_match($regex, $page, $matches);
104 if (!$status) display_error_and_quit(403);
106 // validate directory
107 if (!isset($allowed_dirs[$matches[2]])) {
108 // maybe one of its parent directories had a recursive declaration
109 $dir = $matches[2];
110 $dirs = explode('/', $dir);
111 $test_dir = '';
112 $ok = false;
113 foreach ($dirs as $name) {
114 if ($name === '') break;
115 if (
116 isset($allowed_dirs[$test_dir]) &&
117 $allowed_dirs[$test_dir] === 1
119 $ok = true;
120 break;
121 } else {
122 $test_dir .= $name . '/';
125 if (!$ok) display_error_and_quit(403);
128 return $matches[1] . '.xhtml';
132 * Validates the source XHTML, quits if non-existent
133 * @param $page_src filename of page source from validate_page()
135 function validate_page_source($page_src) {
136 if (!is_file($page_src) || !is_readable($page_src)) {
137 // Apache may have redirected to an ErrorDocument which got directed
138 // via mod_rewrite to us, in that case, output the corresponding
139 // status code. Otherwise, we can give the regular 404.
140 $code = (int) getenv("REDIRECT_STATUS");
141 if (!$code || $code == 200) $code = 404;
142 display_error_and_quit($code);
147 * Attempts to use cached version of HTML as output, halts execution
148 * if successful.
149 * @param $page filename of page, already validated
150 * @param $page filename of page source, already validated
152 function try_cache($page, $page_src) {
153 if (
154 !isset($_GET['purge']) &&
155 is_file($page) &&
156 !is_cache_stale($page, $page_src)
158 // cached version is fresh, serve it. This shouldn't happen normally
159 set_response_code(200); // if we used ErrorDocument, override
160 readfile($page);
161 exit;
166 * Using filemtime, determines whether or not cache is old.
167 * @warning Cache file must exist! Check for it with is_file($page)
168 * @param $page filename of page
169 * @param $page filename of page source
171 function is_cache_stale($page, $page_src) {
172 $mtime_page = filemtime($page);
173 $mtime_page_src = filemtime($page_src);
174 return $mtime_page_src > $mtime_page;
178 * Determines whether or not an .html file was generated by us
179 * @param $page filename of page, must exist
181 function is_created_by_us($page) {
182 $contents = file_get_contents($page);
183 return (strpos($contents, '<!-- generated by HTML Compiler -->') !== false);
187 * Generates a page from the source file
188 * @param $page Filename of output page
189 * @param $page_src Filename of source page
190 * @return Output page text
192 function generate_page($page, $page_src) {
193 $source = file_get_contents($page_src);
194 $contents = $source . '<!-- generated by HTML Compiler -->'; // do processing
195 file_put_contents($page, $contents);
196 return $contents;
200 * Traverses a directory looking for pages with a particular extension.
201 * @param $ext Extension we're looking for, needs period
202 * @param $dir Directory we're looking in
203 * @param $recursive Whether or not to go recursively
205 function scan_for_pages($ext, $dir, $recursive) {
206 $scandir = ($dir === '') ? './' : $dir; // scandir barfs on ''
207 $files = array();
208 if (is_array($ext)) {
209 foreach ($ext as $i => $e) $files[$i] = array();
211 $contents = scandir($scandir);
212 foreach ($contents as $file) {
213 if (empty($file) || $file[0] == '.') continue;
214 if (is_dir($dir . $file) && $recursive) {
215 $files = array_merge($files, scan_for_pages($ext, $dir . $file . '/', 1));
216 continue;
218 $fileext = strrchr($file, ".");
219 if (is_array($ext)) {
220 foreach ($ext as $i => $ext_single) {
221 if ($fileext === $ext_single) {
222 $files[$i][] = $dir . $file;
225 } else {
226 if ($fileext === $ext) {
227 $files[] = $dir . $file;
231 return $files;
235 * Traverses an array of directories for files with extension
236 * @param $ext Extension we're looking for, needs period
237 * @param $dir Directories that are allowed, see hc-config.default.php
239 function scan_dirs_for_pages($ext, $allowed_dirs) {
240 $files = array();
241 if (is_array($ext)) {
242 foreach ($ext as $i => $e) $files[$i] = array();
244 foreach ($allowed_dirs as $dir => $recursive) {
245 $pages = scan_for_pages($ext, $dir, $recursive);
246 if (is_array($ext)) {
247 foreach ($ext as $i => $e) {
248 $files[$i] = array_merge($pages[$i], $files[$i]);
250 } else {
251 $files = array_merge($pages, $files);
254 return $files;