source: t29-www/lib/host.php @ 930

Last change on this file since 930 was 809, checked in by sven, 9 years ago

Backend: Bugfixes beim Hosterkennungssystem. Externe Hosts funktionieren jetzt etwas besser.

File size: 13.7 KB
Line 
1<?php
2/**
3 * t29v6 new Hostinfo and Hosthook system.
4 *
5 * Local host.php files in the webroot can hook into t29* php
6 * and js classes without interfering the code. This can be usually
7 * be used for extra svn information.
8 * Hostinfos can also be appended in this file and therefore be
9 * managed centrally.
10 *
11 * webroot/host.php muss have a t29LocalHost extends t29Host class.
12 *
13 **/
14
15abstract class t29Host {
16        const webroot_host_file = '/host.php'; # relative to webroot
17        const webroot_local_host_classname = 't29LocalHost';
18        const env_hidesuffix_name = "T29URLHIDESUFFIX";
19
20        /// $hostname: An identifier like a FQDN. Is only used to identify the t29Host instance and not
21        ///            for constructing any URL.
22        /// This value must be overwritten in child classes!
23        public $hostname = "undefined";
24       
25        /// $document_root := realpath($_SERVER['DOCUMENT_ROOT']), performed by setup().
26        ///                   Can be used to identify the unix file path to the webserver docroot of the
27        ///                   webhost. Independent of the t29 system.
28        public $document_root;
29       
30        /// $webroot: The unix file path to the t29 web installation, actually the parent directory
31        ///           of the lib/ directory. Is widely used by many files and also computed by themselves.
32        public $webroot;
33        /// $lib  := realpath(__FILE__), just for convenience. $lib = "$webroot/lib" always holds.
34        public $lib;
35       
36        /// $web_prefix: The URL path to the webroot. Example:
37        ///              http://example.com/path/to/t29/de/page.php
38        ///                                ^^^^^^^^^^^^
39        ///                            This part is the web prefix, if /de/page.php is the script_filename.
40        /// This value is computed by setup().
41        public $web_prefix = "";
42       
43        /// $has_web_prefix := !empty($web_prefix). If this host is installed in root or not
44        public $has_web_prefix = false;
45       
46        /// $script_filename: The t29-internal identifying url path, like "/de/page.php" or "/en".
47        ///                   While $_SERVER['SCRIPT_FILENAME'] still contains the $web_prefix, this
48        ///                   string is sanitized.
49        /// This value is computed by setup().
50        public $script_filename;
51
52        /// $slash_filename: Cross-platform $script_filename, always starting with a "/".
53        public $slash_filename;
54
55        /// $ressources: CSS and JavaScript file paths ("Assets"), as used by the RessourceLoader,
56        ///              the loader.php and technikum29.php entry points and the Template.
57        private function ressources_array($webroot='') {
58                // this is implemented as method, because of
59                // 1. "$webbroot/..." like strings. This isn't a good idea anyway (e.g. in ressourceloader: $module_dir_rel2webroot
60                // 2. Closures: function($conf){...} doesn't work as class attribute.
61                // This is rather dirty, but anyway the supposed way to access these data is the public get_ressources().
62                $ressources = array(
63                        'cache_file' => array('compressed.js', 'style.css'),
64                        'module_dir' => array("$webroot/shared/js-v6/modules", "$webroot/shared/css-v6/modules"),
65                        'page_dir' => array("$webroot/shared/js-v6/pagescripts", "$webroot/shared/css-v6/pagestyles"),
66                        'glob_pattern' => array('*.js', '*.css'),
67                        'content_types' => array('application/javascript', 'text/css'),
68                        'class' => array('t29JavaScriptRessourceLoader', 't29StyleSheetRessourceLoader'),
69                        'modules' => function($conf){ return glob($conf['module_dir'] . '/' . $conf['glob_pattern']); },
70                );
71                return $ressources;
72        }
73
74        /// $ressources_types: The Ressources array above consists of numeric arrays. This array
75        ///                    maps those positions (numeric keys) to $conf array positions.
76        ///                    Use get_ressources() to resolve this mapping.
77        private $ressources_types = array('js', 'css');
78
79        public function get_ressources($type, $webroot, $debug_flag=false) {
80                $typepos = array_search($type, $this->ressources_types);
81                if($typepos === FALSE) return null;
82                $conf = array_map(function($val) use($typepos) 
83                        { return is_array($val) ? $val[$typepos] : $val; },
84                        $this->ressources_array($webroot));
85                $conf['type'] = $type;
86                // callback functions need the $conf we built.
87                $conf['modules'] = call_user_func($conf['modules'], $conf);
88                $conf['debug'] = $debug_flag; // skip cache and just concat everything
89                return $conf;
90        }
91
92        /**
93         * A special helper class, used by t29Template and technikum29.php entry point.
94         * The general (clean) way would be querying get_ressources().
95         * There is also t29RessourceLoader::get_page_specific_urls() which basically does
96         * the same, just using the global conf array.
97         **/ 
98        public function ressources_get_pagestyle($seiten_id) {
99                $ressources =  $this->ressources_array();
100                // We address the css property directly with the [1] index. Bad!
101                return $ressources['page_dir'][1] . '/' . $seiten_id . '.css';
102        }
103
104        /// Singleton fun for detect()
105        private static $instance;
106        private static function new_singleton($classname) {
107                if(!isset(self::$instance))
108                        self::$instance = new $classname;
109                return self::$instance;
110        }
111
112        /**
113         * Factory for creating a t29Host instance automatically
114         * from the current host. This method will decide which
115         * subclass has to be taken.
116         * This function als implements the Singleton pattern, so
117         * you can call it frequently.
118         **/
119        static function detect() {
120                $instance = null;
121
122                // check if local host file exists
123                $hostfile = dirname(__FILE__) . '/../' . self::webroot_host_file;
124                if(file_exists($hostfile)) {
125                        include $hostfile;
126                        if(class_exists(self::webroot_local_host_classname)) {
127                                $x = self::webroot_local_host_classname;
128                                $host = self::new_singleton($x);
129                                $host->setup();
130                                return $host;
131                        } else {
132                                print "Warning: Hostfile $hostfile does not contain class ".self::webroot_local_host_classname."\n";
133                        }
134                }
135               
136                // Quick and Dirty: Load hostname specific instances
137                switch($_SERVER['SERVER_NAME']) {
138                        case 'heribert':
139                        case 'localhost':
140                                $localhost = self::new_singleton('t29HeribertHost');
141                                $localhost->setup();
142                                return $localhost;
143                }
144               
145                $publichost = self::new_singleton('t29PublicHost');
146                $publichost->setup();
147                return $publichost;
148        }
149
150        /**
151         * "Factory" for creating an instance of $className as a singleton.
152         * You should really use detect() for auto-detection instead of
153         * forcing a host.
154         * Only useful for explicitely calling t29ExternalHost by $external call.
155         **/
156        static function create($className) {
157                return self::new_singleton($className)->setup();
158        }
159       
160        /**
161         * A constructing method which is always called by the t29Host::detect() factory.
162         * It does some general stuff.
163         * Of course you can always write your own setup() class - it's just your __constructor.
164         * The constructor will of course be called before the setup() method.
165         *
166         * This method detects two things:
167         *   1. if this host does Clean URLs (Suffix rewriting)
168         *   2. if this host is *not* installed in its own virtualhost (i.e. on docroot).
169         *
170         * Note:
171         * This is only public as it must be able to be overwritten by subclasses.
172         * This should be a constructor!
173         *
174         * @returns $this for chaining
175         **/
176        public function setup() {
177                $this->is_rewriting_host = isset($_SERVER[self::env_hidesuffix_name]);
178               
179                $this->lib = dirname(__FILE__);
180                $this->webroot = realpath($this->lib . '/../');  # file path to root of t29 web installation
181               
182                /*
183                   calculate the web_prefix. This is kind of a detection.
184                   
185                   Examples for an installation in Document root:
186                      $lib = /var/www/technikum29.de/lib
187                      $webroot = /var/www/technikum29.de
188                      $_SERVER["DOCUMENT_ROOT"] = /var/www/technikum29.de
189                      $this->document_root = /var/www/technikum29.de
190                      $_SERVER["SCRIPT_FILENAME"] = /var/www/technikum29-www/de/index.php
191                      $this->script_filename = /de/index.php
192                      $_SERVER["REQUEST_URI"] = /de
193                      $_SERVER["SCRIPT_NAME"] = /de/index.php
194                      $web_prefix = ""
195                     
196                   Example for an installation in an arbitrary directory below Document Root:
197                     $lib = /var/www/arbitrary/lib
198                     $webroot = /var/www/arbitrary
199                     $_SERVER['DOCUMENT_ROOT'] = /var/www
200                     $this->document_root = /var/www/arbitrary
201                     $_SERVER['SCRIPT_FILENAME'] = /var/www/arbitrary/de/index.php
202                     $this->script_filename = /arbitrary/de/index.php
203                     $_SERVER['REQUEST_URI'] = /arbitrary/de
204                     $_SERVER['SCRIPT_NAME'] = /arbitrary/de/index.php
205                     $web_prefix = "/arbitrary"
206                     
207                   Example for an installation in mod_userdirs homedir out of Docroot:
208                     $lib = /home/sven/public_html/foo/lib
209                     $webroot = /home/sven/public_html/foo
210                     $_SERVER['DOCUMENT_ROOT'] = /var/www   (mind that!)
211                     $this->document_root = /home/sven/public_html/foo
212                     $_SERVER['SCRIPT_FILENAME'] = /~sven/foo/en/index.php
213                     $this->script_filename = /~sven/foo/en/index.php
214                     $_SERVER['REQUEST_URI'] = /~sven/foo/en/
215                     $_SERVER['SCRIPT_NAME'] = /~sven/foo/en/index.php
216                     $web_prefix = "/~sven/foo"
217                */
218
219                // this algorithm is good for detecting paths below the document root.
220                // it is not suitable for paths out of the document root
221                $this->document_root = realpath($_SERVER['DOCUMENT_ROOT']);
222                if($this->webroot == $this->document_root) {
223                        // we are installed in document root
224                        $this->web_prefix = "";
225                } else {
226                        // we are installed in some arbitary directory
227                        $this->web_prefix = substr($this->webroot, strlen($this->document_root));
228                }
229               
230                // TODO: Somehow autodetect paths out of the document root
231               
232                $this->has_web_prefix = !empty($this->web_prefix);
233               
234                //print "Web prefix:<pre>";
235                //var_dump($this); exit;
236                   
237                $this->script_filename = substr(realpath($_SERVER['SCRIPT_FILENAME']), strlen($this->document_root)); # e.g.: "/de/page.php"
238
239                // Windows realpath() converts Unix Paths ($_SERVER) to Windows Paths (like \en\index.php).
240                // We want to use unix paths ($_SERVER like) internally, so do this dummy conversion back, if neccessary
241                $this->script_filename = str_replace('\\', '/', $this->script_filename);
242               
243                # Bug when DOCUMENT_ROOT ends with trailing slash: make sure $file begins with /:
244                $this->slash_filename = $this->script_filename;
245                if($this->slash_filename{0} != '/') $this->slash_filename = '/'.$this->slash_filename;
246       
247                //phpinfo(); exit;
248                return $this; // Chaining
249        }
250       
251        function check_url_rewrite() {
252                if($this->is_rewriting_host) {
253                        $path = $_SERVER['REQUEST_URI'];
254                        $newpath = $this->rewrite_link($path);
255                        if($path != $newpath) {
256                                header('HTTP/1.1 301 Moved Permanently');
257                                header('Location: '.$newpath);
258                                return $newpath;
259                        }
260                }
261                return null;
262        }
263
264        public function __toString() {
265                return 't29v6/'.$this->hostname;
266        }
267       
268        /**
269         * Rewrite Links so they match for this host.
270         * This method acts like a pipeline:
271         *  $new_link = rewrite_link($old_link);
272         * It can perform two conversions:
273         *
274         *   1. Rewriting/Clean URL system: Will strip file suffixes, if appropriate.
275         *      This will be done whenever this is a rewriting host and this is the
276         *      main purpose for this function.
277         *
278         *   2. Prefixing the correct web prefix. This is *only* be done when
279         *      $also_rewrite_prefix = true. The reaseon is that prefix rewriting is
280         *      generally done by a global page rewrite after generation of the whole
281         *      page on a whole-page-level. This is less error prone.
282         *      Anyway you can use this function if you think you need. blubblubb
283         *
284         *
285         **/
286        function rewrite_link($link_target, $also_rewrite_prefix=false) {
287                // rewrite link if neccessary. This function will be called hundreds of times
288                // while rendering a page, rewriting all links found.
289               
290                // pending: prefix setzen.
291                if($this->has_web_prefix && $also_rewrite_prefix) {
292                        $link_target = $this->web_prefix . $link_target;
293                }
294               
295                if($this->is_rewriting_host) {
296                        $new_target = preg_replace('/\.(?:php|shtml?)([#?].+)?$/i', '\\1', $link_target);
297                        return $new_target;
298                } else {
299                        // just the identity function
300                        return $link_target;
301                }
302               
303        }
304       
305        function get_shorthand_link_returner() {
306                $t = $this;
307                return function($link_target)use($t) { return $t->rewrite_link($link_target); };
308        }
309
310        abstract function fillup_template_conf(&$template_conf);
311}
312
313class t29PublicHost extends t29Host {
314        /**
315         * This is actually the default public host which should be loaded
316         * at www.technikum29.de.
317         **/
318        public $hostname = "public";
319        function fillup_template_conf(&$template_conf) {}
320}
321
322/**
323 * Host auf heriberts Rechner; dort wird ein weiterer Metatag mit id eingefuehrt,
324 * mit dem seine Firefox Editthispage-Extension die Seite bearbeiten kann.
325 **/
326class t29HeribertHost extends t29Host {
327        public $hostname = "heribert";
328
329        function fillup_template_conf(&$template_conf) {
330                $template_conf['header_prepend'][] = 
331                        '<meta name="t29.localfile" content="'.$_SERVER['SCRIPT_FILENAME'].'" id="localFileSource">';
332        }
333}
334
335/**
336 * Ein "external" Host, der Links mit voller URL angeben muss, etwa bei der
337 * Einbindung von CSS/JS, aber auch von Links.
338 * Genutzt fuer $external-Einbindung von technikum29.php
339 **/
340class t29ExternalHost extends t29Host {
341        public $hostname = "external";
342        public $target_host = 'http://www.technikum29.de';
343
344        function rewrite_link($link_target, $also_rewrite_prefix=false) {
345                $link_target = parent::rewrite_link($link_target, $also_rewrite_prefix);
346       
347                if($also_rewrite_prefix) {
348                        // check if link has host part
349                        if(!preg_match('#^http:#i', $link_target)) {
350                                $sep = ($link_target{0} == '/') ? '' : '/';
351                                $link_target = $this->target_host . $sep . $link_target;
352                        }
353                }
354
355                return $link_target;
356        }
357
358        function fillup_template_conf(&$template_conf) {
359                $template_conf['header_prepend'][] = 
360                        '<meta name="t29.host.from" content="'.$_SERVER['SERVER_NAME'].'">';
361        }
362}
Note: See TracBrowser for help on using the repository browser.
© 2008 - 2013 technikum29 • Sven Köppel • Some rights reserved
Powered by Trac
Expect where otherwise noted, content on this site is licensed under a Creative Commons 3.0 License