- Fix bug in page validation that would result in directories sharing prefixes being...
[xhtml-compiler.git] / XHTMLCompiler / Page.php
blob9258b27a9e66154bd61c4d71906d9cfd0cba8abe
1 <?php
3 // TODO: Documentation
4 class XHTMLCompiler_Page
7 protected $pathStem;
9 protected $sourceExt = '.xhtml';
10 protected $cacheExt = '.html';
12 public function __construct($path) {
13 // TODO: Use pathinfo() rather than our adhoc path parsing
14 // TODO: Factor out allowed_dir realpath'ing to config class
15 // TODO: Clean this up
17 $xc = XHTMLCompiler::getInstance();
18 // test file extension, must be html or xhtml
19 $ext = strrchr($path, '.');
20 if ($ext !== $this->sourceExt && $ext !== $this->cacheExt) {
21 return display_error_and_quit(403);
23 $dir = dirname($path);
24 // test for directory's existence, simultaneously resolve
25 // to full path
26 $dir = $xc->realpath($dir);
27 if ($dir === false) return display_error_and_quit(403);
28 $allowed_dirs = $xc->getConf('allowed_dirs');
29 $ok = false;
30 foreach ($allowed_dirs as $allowed_dir => $recursive) {
31 $allowed_dir = $xc->realpath($allowed_dir);
32 if ($allowed_dir === false) continue;
33 if ($dir === $allowed_dir) {
34 $ok = true;
35 break;
36 // slash is required to prevent $allowed_dir = 'subdir' from
37 // matching $dir = subdirectory, thanks Mordred!
38 } elseif (strpos($dir, $allowed_dir . '/') === 0 && $recursive) {
39 $ok = true;
40 break;
43 if (!$ok) return display_error_and_quit(403);
45 $this->pathStem = substr($path, 0, strrpos($path, '.'));
47 if (!$xc->isFile($this->getSourcePath())) {
48 // Apache may have redirected to an ErrorDocument which got directed
49 // via mod_rewrite to us, in that case, output the corresponding
50 // status code. Otherwise, we can give the regular 404.
51 $code = $xc->getRedirectStatus();
52 if (!$code || $code == 200) $code = 404;
53 display_error_and_quit($code);
57 public function getPathStem() {
58 return $this->pathStem;
60 public function getCachePath() {
61 return $this->pathStem . $this->cacheExt;
63 public function getSourcePath() {
64 return $this->pathStem . $this->sourceExt;
67 public function getCache() {
68 return file_get_contents($this->getCachePath());
70 public function getSource() {
71 return file_get_contents($this->getSourcePath());
74 public function isCacheExistent() {
75 return is_file($this->getCachePath());
77 public function isSourceExistent() {
78 return is_file($this->getSourcePath());
80 public function isCacheStale() {
81 if (!$this->isCacheExistent()) {
82 throw new Exception('Cannot check for stale cache when cache
83 does not exist, please call isCacheExistent and take
84 appropriate action with the result');
86 $mtime_cache = filemtime($this->getCachePath());
87 $mtime_source = filemtime($this->getSourcePath());
88 return $mtime_source > $mtime_cache;
90 public function writeCache($contents) {
91 file_put_contents($this->getCachePath(), $contents);
93 public function tryCache() {
94 if (
95 !isset($_GET['purge']) &&
96 $this->isCacheExistent() &&
97 !$this->isCacheStale()
98 ) {
99 // cached version is fresh, serve it. This shouldn't happen normally
100 set_response_code(200); // if we used ErrorDocument, override
101 readfile($this->getCachePath());
102 return true;
105 public function generate() {
106 $source = $this->getSource();
107 $contents = $source . '<!-- generated by HTML Compiler -->'; // do processing
108 $this->writeCache($contents);
109 return $contents;
112 public function display() {
113 if($this->tryCache()) return;
114 echo $this->generate();