1b23dd6082c4332abf7a991ffdab48ee8598ff52
[xhtml-compiler.git] / functions.php
blob1b23dd6082c4332abf7a991ffdab48ee8598ff52
1 <?php
3 /**
4 * Returns true if in cli mode
5 */
6 function is_cli() {
7 return php_sapi_name() === 'cli';
10 /**
11 * Convenience function that outputs an HTTP status code
12 * @param $code integer HTTP status code
13 * @return string HTTP status code's name
15 function set_response_code($code) {
16 $php = XHTMLCompiler::getPHPWrapper();
17 $code_descriptions = array( // could be factored out
18 200 => 'Okay',
19 304 => 'Not Modified',
20 401 => 'Unauthorized',
21 403 => 'Forbidden',
22 404 => 'Not Found',
23 500 => 'Internal Server Error',
24 503 => 'Service Unavailable',
26 $code = (int) $code; // enforce integer
27 $text = (string) $code;
28 if (isset($code_descriptions[$code])) {
29 $text .= ' ' . $code_descriptions[$code];
31 if (isset($_SERVER['SERVER_PROTOCOL'])) {
32 $php->header($_SERVER['SERVER_PROTOCOL'] . ' ' . $text, true, $code);
33 $php->header('Status: ' . $text);
35 return $text;
38 /**
39 * Determines the requested page from server environment files
40 * @return string page name
42 function get_page_from_server() {
43 $php = XHTMLCompiler::getPHPWrapper();
44 // load up page from environment variables, if using ErrorDocument impl.
45 $page = $php->getRequestURI();
46 $self = dirname($php->getPHPSelf());
47 $root = strlen(substr($self, 0, strrpos($self, '/')));
48 $page = substr($page, $root + 1); // remove leading slash and root
49 return $page;
52 /**
53 * Determines the requested page from get parameter
54 * @return string page name
56 function get_page_from_get() {
57 $php = XHTMLCompiler::getPHPWrapper();
58 return $php->getGVal('f');
61 /**
62 * Takes a page name and appends index filename if necessary
63 * @param $page string page name
64 * @param $directory_index string name of file to use as directory index
66 function normalize_index($page, $directory_index) {
67 $php = XHTMLCompiler::getPHPWrapper();
68 if ($page == '') $page = $directory_index;
69 if ($php->isDir($page)) {
70 if ($page[strlen($page)-1] !== '/') $page .= '/';
71 $page .= $directory_index;
73 return $page;
76 /**
77 * Determines whether or not an .html file was generated by us
78 * @param $page filename of page, must exist
80 function is_created_by_us($page) {
81 $contents = file_get_contents($page);
82 return (strpos($contents, '<!-- generated by XHTML Compiler -->') !== false);
85 /**
86 * Exception handler, prints a pretty HTTP error message
87 * @param $e The uncaught exception
88 * @todo Augment debug mode to have stack traces
89 * @todo Make more friendly to command-line
91 function xhtmlcompiler_exception_handler(Exception $e) {
92 $xc = XHTMLCompiler::getInstance();
93 $php = XHTMLCompiler::getPHPWrapper();
95 if (is_cli()) {
96 $php->paint((string) $e);
97 return;
100 $error_xsl = $xc->getConf('error_xsl');
102 // extract information out of exception
103 if ($e instanceof XHTMLCompiler_Exception) {
104 $code = $e->getCode();
105 $title = $e->getMessage();
106 $details = $e->getDetails();
107 } else {
108 $code = 500;
109 $title = false;
110 $details = $e->getMessage();
113 // send appropriate response code
114 $default = set_response_code($code);
116 // munge the title
117 if (!$title) $title = $default;
118 else $title = $code . ' ' . $title;
120 // build a error document
121 $page = new DOMDocument('1.0', 'utf-8');
122 $error = $page->createElement('error'); $page->appendChild($error);
123 $error->appendChild($page->createElement('code', $code));
124 $error->appendChild($page->createElement('title', $title));
125 $error->appendChild($page->createElement('base', $xc->getConf('web_path')));
127 // details (in HTML)
128 if ($details) {
129 $html = $page->createDocumentFragment();
130 $html->appendXML($details);
131 $details_dom = $page->createElement('details');
132 $details_dom->appendChild($html);
133 $error->appendChild($details_dom);
136 // debug
137 if ($xc->getConf('debug')) {
138 $debug = $page->createElement('debug');
139 $debug->appendChild($page->createElement('base-dir', $base = dirname(__FILE__)));
140 $debug->appendChild($page->createElement('file', shortenFile($e->getFile(), $base)));
141 $debug->appendChild($page->createElement('line', $e->getLine()));
142 $error->appendChild($debug);
145 // generate and output html
146 $xslt_processor = new ConfigDoc_HTMLXSLTProcessor();
147 $xslt_processor->importStylesheet(dirname(__FILE__) . '/../' . $error_xsl);
148 $html = $xslt_processor->transformToHTML($page);
149 $php->paint($html);
154 /** Returns short filename based on a base directory */
155 function shortenFile($file, $base) {
156 if (strpos($file, $base) === 0) {
157 $file = str_replace('\\', '/', substr($file, strlen($base)));
158 if ($file === '') $file = '.';
159 if ($file[0] === '/') $file = substr($file, 1);
161 return $file;
165 * Retrieves the last $limit log entries.
166 * @param $repos_url Repository URL of item to get logs for
167 * @param $limit Integer limit of items
169 function svn_log_limit($repos_url, $limit, $page = null) {
170 $limit = (int) $limit;
171 if ($limit <= 0) return array();
172 // attempt the cache
173 $cache_filename = 'xhtml-compiler/cache/svn/log/' . md5($repos_url) . '.ser';
174 $cached_rev = false;
175 if ($page && file_exists($cache_filename)) {
176 $logs = unserialize(file_get_contents($cache_filename));
177 $cached_rev = !empty($logs[0]) ? $logs[0]['rev'] : false;
178 // determine current revision number
179 $rev = $page->getSVNRevision();
180 if ($cached_rev == $rev && count($logs) == $limit) return $logs;
182 // -q flag used to prevent server from sending log messages
183 // most recent is retrieved first
184 $output = shell_exec("svn log -q --limit $limit $repos_url");
185 preg_match_all('/^r(\d+) /m', $output, $matches);
186 $ret = array();
187 foreach ($matches[1] as $rev) {
188 // reuse previously cached entries
189 if ($rev <= $cached_rev) {
190 $ret[] = array_shift($logs);
191 continue;
193 $log = svn_log($repos_url, (int) $rev);
194 $ret[] = $log[0]; // log is only one item long
196 // save to cache
197 file_put_contents($cache_filename, serialize($ret));
198 return $ret;
202 * Checks for errors, and sends a notification email if necessary
204 function check_errors() {
205 $xc = XHTMLCompiler::getInstance();
206 $error_log = $xc->getConf('error_log');
207 $error_mute = $xc->getConf('error_mute');
208 if (file_exists($error_log) && filesize($error_log) > 0) {
209 if (file_exists($error_mute) && filesize($error_mute) == 0) {
210 $error_text = wordwrap(file_get_contents($error_log), 70, "\r\n");
211 try {
212 $message = Swift_Message::newInstance()
213 ->setSubject('Errors in XHTML Compiler')
214 ->setTo($xc->getConf('admin_email'))
215 ->setFrom($xc->getConf('from_email'))
216 ->setBody(<<<BODY
217 Some errors occured in the log. No further messages
218 will be sent until the mute file [1] is deleted. The
219 error log file [2] will be blanked after resolving any
220 issues.
222 [1] $error_mute
223 [2] $error_log
225 $error_text
226 BODY
228 $xc->getConf('smtp_transport')->send($message);
229 } catch (Exception $e) {
230 // uh oh, mail failed, not much we can do about that.
231 if ($xc->getConf('debug')) {
232 echo $e . "\n";
235 file_put_contents($error_mute, '1');
236 } elseif (!file_exists($error_mute) && file_exists($error_log)) {
237 $tentative_new_name = "$error_log." . date('Ymd');
238 $i = 0;
239 $new_name = $tentative_new_name;
240 while (file_exists($tentative_new_name) && file_exists($new_name)) {
241 $new_name = "$tentative_new_name-$i";
242 $i++;
244 copy($error_log, $new_name);
245 file_put_contents($error_log, '');
246 file_put_contents($error_mute, '');