3v4l.org

run code in 150+ php & hhvm versions
Bugs & Features
<?php /* * load_config() loads configuration options from a ini file located at the * path given .This file is parsed with sections enabled and the configuration * options for the main cms are located in the `[cms]' section. White and black * lists are to be provided as a single string seperated by comma (`,'). Theses * will be transformed into arrays and joined with the default list items. The * function returns a multi-dimensional array with sections (only 'cms' for now) * as keys on the first level and option names as keys in the second level. * * $config_path * string path to the configuration file to be loaded */ function load_config($config_path){ $config = parse_ini_file($config_path, true); if(!isset($config['cms'])){ $config['cms'] = array(); } // just set the timezone immediately if(isset($config['cms']['timezone'])){ date_default_timezone_set($config['cms']['timezone']); } else { date_default_timezone_set("Europe/Berlin"); } if(!isset($config['cms']['global_root_dir'])){ $config['cms']['global_root_dir'] = '/home/ps0ke/Projects/Greiner/global_root_dir/'; } if(isset($config['cms']['directory_black_list'])){ $config['cms']['directory_black_list'] = explode(',', $config['cms']['directory_black_list']); } else { $config['cms']['directory_black_list'] = array(); } $config['cms']['directory_black_list'] = array_merge( $config['cms']['directory_black_list'], array('.', '..', '.git', 'mth-cms') ); if(isset($config['cms']['file_name_black_list'])){ $config['cms']['file_name_black_list'] = explode(',', $config['cms']['file_name_black_list']); } else { $config['cms']['file_name_black_list'] = array(); } $config['cms']['file_name_black_list'] = array_merge( $config['cms']['file_name_black_list'], array('index') ); if(isset($config['cms']['file_extension_white_list'])){ $config['cms']['file_extension_white_list'] = explode(',', $config['cms']['file_extension_white_list']); } else { $config['cms']['file_extension_white_list'] = array(); } $config['cms']['file_extension_white_list'] = array_merge( $config['cms']['file_extension_white_list'], array('html') ); /* * Automatically enable `php' as a valid extension if its execution is * enabled. It would be better to use `!==' and `===' for checking for * true boolean types. This would prevent logic errors due to PHP's * automatic type conversion (e.g. a non empy string like a wrongly * splelled `false' in the config would evaluate to true). Unfortune- * nately this doesen't work and I suspect that parse_ini_file() does * not set true boolean types. */ if(!isset($config['cms']['execute_php']) || $config['cms']['execute_php'] != true ){ $config['cms']['execute_php'] = false; } if($config['cms']['execute_php'] == true){ array_push( $config['cms']['file_extension_white_list'], 'php' ); } return $config; } /* * link_from_path() generates a link (no full URL) from a path to a local file. * It defaults to the language set globally, but you can set a language manu- * ally. * * $path * the path of the file to be linked to. Starting `.' and `/' will be re- * moved. * $language * the language of the page the the link poits to. This will default to the * language set globally. Should be a two-letter country code. For now * either `en' or `de'. */ function link_from_path($path, $language=null){ /* * I am not really sure which variable to use: SCRIPT_URI and SCRIPT_URL * are not guaranteed to be available on all systems. SCRIPT_NAME seems to * be a safe choice, but I don't know how flexible and robust this is. * CONTEXT_PREFIX would probably be the best choice but is also not * available on all systems. * * relative links * - SCRIPT_NAME * - SCRIPT_URL * - CONTEXT_PREFIX * * absolute links * - SCIPT_URI */ $base = pathinfo($_SERVER['SCRIPT_NAME'], PATHINFO_DIRNAME); $base = ltrim($base, '/'); $base = rtrim($base, '/'); if(is_null($language)){ if('en' == $GLOBALS['lang']){ $lang = 'en/'; } else { $lang = ''; } } else { if('de' == $language){ $lang = ''; } else { $lang = $language.'/'; } } $path = ltrim($path, '.'); $path = ltrim($path, '/'); return "/$base/{$lang}page/$path"; } /* * language_toggle_html_link() renders an html string containing a link to * the other language version of the page. It uses the language set globally. * Note: It does *not* check, wether the language is actually available! * * $path * defaults to the GET path, the path for the page. * $lang * defaults to the opposite of the global language. The language of the page 8 the link points to. */ function language_toggle_html_link($path=null, $lang=null){ if(is_null($path)){ $path = $GLOBALS['path']; } if(is_null($lang)){ $lang = $GLOBALS['lang']; } if('en' == $lang){ $other_lang_link = link_from_path($path, 'de'); $other_lang_display = 'Deutsch'; } else { $other_lang_link = link_from_path($path, 'en'); $other_lang_display = 'English'; } echo "<a href=\"$other_lang_link\">$other_lang_display</a>"; } /* * Returns a boolean value if the *other* language is available. This is * intended to be used in templating to only show the language toggle when * available. This function is language aware. * * $path * the path of the page to check availability for. Defaults to the global * GET path * $lang * the *current* (!) language. This function will check availability of the * *counter part* language. E.g. if $lang is 'de', * language_toggle_available() will return `true' if the page at $path is * available in 'en'! */ function language_toggle_available($path=null, $lang=null){ if(is_null($path)){ $path = $GLOBALS['path']; } if(is_null($lang)){ $lang = $GLOBALS['lang']; } if('en' == $lang){ $file = file_name_from_path($path, 'de'); } else { $file = file_name_from_path($path, 'en'); } return file_exists($file); } /* * Generates the actual file name to render from the input path. This respects * the global language value if not given otherwise and will return index.html * files for directories. * * $path * the path to create the file name for. May be a directory * $lang * if given, the filename will have this language suffix */ function file_name_from_path($path, $lang=null){ if(is_null($lang)){ $lang = $GLOBALS['lang']; } if(!file_exists($path)){ return null; } // If a directory is requested, try to serve its index.html if(is_dir($path)){ $file = rtrim($path, '/').'/index.html'; } else { $file = $path; } // Construct the english version file name. if('de' != $lang){ $info = pathinfo($file); $file = $info['dirname'].'/'.$info['filename'].".$lang.".$info['extension']; } return $file; } /* * The Node class contains a linked list of files or directories, which is * build recursivly when the object is created. * * $path * contains the path to the node, relative to the path the top-most Node * was initialized with, as a string. * $display_name * contains the name that shall be rendered. Will be set by the user. A * string. * $display_name_en * contains the render-name for the english version of the file * $display_position * is an integer index relevant for the ordering of the menu items during * rendering. Will be set by the user * $is_directory * is a boolean value wether the Node is a file or a directory; this * possibly containing sub nodes * $sub_nodes * is null if there are no sub nodes; Otherwise it is an array containing * additional Node objects. * $visible * is a boolean value wether the Node should be added to to the sub-nodes * and therefore be rendered in the navigation. It can be set by the user * via the ini configuration but defaults to true. * * $navigation_first * is a boolean value wether the Node should get the `first' class in * rendering of the navigation */ class Node { public $path; public $display_name; public $display_name_en = null; public $display_position; public $is_directory; public $sub_nodes = null; public $visible = true; private $navigation_first = false; function __construct($path){ if(!file_exists($path)){ throw new Exception("$path does not exist."); } $this->path = $path; $this->is_directory = is_dir($path); $this->sub_nodes = $this->sub_nodes(); /* * Try to parse the display information from the ini-section. Fall back * to file name if the information was not set. parse_ini_file() is * called silent to surpress warnings if file does not exist. If the * node is a file, the file is read and the first comment block is * parsed as an ini-section. */ if($this->is_directory){ $ini = @parse_ini_file($this->path.'/dir.ini'); } else { $lines = file($this->path); $ini_array = array(); foreach($lines as $line){ if("<!--\n" == $line){ continue; } else if("-->\n" == $line){ break; } else { array_push($ini_array, $line); } } $ini = @parse_ini_string(implode($ini_array)); } if(isset($ini['name'])){ $this->display_name = $ini['name']; } else { $this->display_name = pathinfo($path)['filename']; } if(isset($ini['position'])){ $this->display_position = $ini['position']; } else { $this->display_position = 1000; } if(isset($ini['visible'])){ $this->visible = $ini['visible']; } if(isset($ini['name_en'])){ $this->display_name_en = $ini['name_en']; } } /* * The build_navigation() function recursivly builds a directory tree from * the node with nested html unordered lists. It renderes content to the * page. Only top nodes for each level in the current path are shown. This * is achieved by testing the current Node path against each part of the * requested path and its parent directory incrementally. * Links will point to english version if viewed from an english version * page. * * $path * is the GET path. It is used to narrow the drill-down. Only the top * nodes in each level are shown. * $first_run * holds a boolean value wether the function is run for the first time * i. e. has been called by the programmer or if it was called as an * act of recursion by itself. */ public function build_navigation($path, $first_run=true){ $path_array = explode('/', $path); $incremental_path = "."; $visible = false; foreach($path_array as $part){ $incremental_path .= "/".$part; $visible |= dirname($this->path) == dirname($incremental_path); $visible |= dirname($this->path) == $incremental_path; } $expanded = true; if(!$first_run && $visible){ if('en' == $GLOBALS['lang'] && !is_null($this->display_name_en)){ $display_name = $this->display_name_en; } else { $display_name = $this->display_name; } $link = link_from_path($this->path); $classes = ''; if($this->navigation_first){ $classes .= 'first '; } else { $classes .= 'leaf '; } if($this->is_directory){ /* * check if the current node is a parent of the requested node * by checking if its path is a sub string of the requested * path. */ if(strpos('./'.$path, $this->path) === false){ $expanded = false; $classes .= 'collapsed '; } else { $classes .= 'expanded '; } } $classes = rtrim($classes); if('./'.$path == $this->path){ $active = ' class="active"'; } else { $active = ''; } echo "<li class=\"$classes\">"; echo "<a href=\"${link}\" title=\"$display_name\" onfocus=\"blurLink(this);\"$active>"; echo $display_name; echo "</a>\n"; } if($this->is_directory && $this->sub_nodes && $expanded){ /* * The nesting level of a navigation point is calculated simply by * counting the elements in the path. * * Wether the Node is the first in a navigation list is figured out * by using a private variable `$navigation_first' that is set for * each Node. Before recursing each sub node during rendering, the * `$first' variable is set to true, which will set every sub * node's $navigation_first. After setting it (for the first time) * $first is set to false and will set all following $navigation_ * first to false. */ $level = count(explode('/', $this->path)); echo "<ul class=\"menu level-$level\">\n"; $first = true; foreach($this->sub_nodes as $sub_node){ if(!is_null($sub_node)){ $sub_node->navigation_first = $first; $first = false; $sub_node->build_navigation($path, false); } } echo "</ul>\n"; } if(!$first_run && $visible){ echo "</li>\n"; } } /* * find_node_by_path returns the Node object correspronding to the $path. * It recursivly walks the Node-tree and checks if the path is coreect. If * it matches, the Node object is returned, null otherwise. */ public function find_node_by_path($path){ if(rtrim($this->path, '/') == rtrim('./'.$path, '/')){ return $this; } else { if($this->sub_nodes){ foreach($this->sub_nodes as $sub_node){ $ret = $sub_node->find_node_by_path($path); if(!is_null($ret)){ return $ret; } } return null; } else { return null; } } } /* * the sub_nodes() function is called by the constructor and fills the * sub_nodes property with an array of Node objects. Only valid sub_nodes * will be valid. File names and extensions are matched against various * black- and white-lists. */ private function sub_nodes(){ global $config; if($this->is_directory){ $sub_nodes = array(); foreach(glob("{$this->path}/*") as $sub_path){ $add_this_node = true; $pathinfo = pathinfo($sub_path); if(is_dir($sub_path)){ // directory name black-list if(in_array( $pathinfo['filename'], $config['cms']['directory_black_list'] ) ){ $add_this_node = false; } } else { // file extension white-list if(! in_array( $pathinfo['extension'], $config['cms']['file_extension_white_list'] ) || // file name black-list in_array( $pathinfo['filename'], $config['cms']['file_name_black_list'] ) ){ $add_this_node = false; } // exclude english versions of files if( preg_match('/\.en$/', $pathinfo['filename']) ){ $add_this_node = false; } } $new_node = new Node($sub_path); if($add_this_node && $new_node->visible){ $display_position = $new_node->display_position; while(isset($sub_nodes[$display_position])){ $display_position += 1; } $sub_nodes[$display_position] = $new_node; } } ksort($sub_nodes); return $sub_nodes; } else { return null; } } } /* * load_templated loads template files. It looks for them first in a local * directory that every user can change. If it does not find the desired * template there, it looks in a the `global_root_dir' from the config. * This should be a central directory on the webserver that only the admin * has access to. This makes updating the templates easy for thos users that * don't need customization. * * $template * the name of the template to load as a string. The file extension should * not be included here. */ function load_template($template){ global $config; /* * All global variables that hold teh cms' state must be included in this * scope to be available from within the include()ed template file. */ global $path; global $base_node; global $current_node; global $lang; $global_path = rtrim($config['cms']['global_root_dir'], '/').'/'.$template.'.html'; $local_path ="mth-cms/templates/$template.html"; if(file_exists($local_path)){ include($local_path); } else { include($global_path); } } /* * render_content() renders the content in respect to the GET path and the * globally set language. If the file does not exist or only another language is * available, it will transparently render an error page. */ function render_content(){ global $config; global $path; global $lang; $file = file_name_from_path($path); $file_de = file_name_from_path($path, 'de'); if('en' == $lang && !file_exists($file) && file_exists($file_de)){ // English version was not found, but a german version exists. load_template('error_404_other_language_available'); } else if(!file_exists($file)){ // No german version exists load_template('error_404'); } else { /* * If PHP execution is activated in the config and the file is a PHP * file, run it. Otherwise check if the file is encoded as UTF-8. If * not, try to convert it to UTF-8. Do not even try to use * mb_detect_encoding() as it just does not work at all. Therefore * try the one encoding that is expected to be used the most apart from * UTF-8: ISO-8859-15 aka Latin-9. This is the best I can do. */ if($config['cms']['execute_php'] && 'php' == pathinfo($file, PATHINFO_EXTENSION) ){ include($file); } else { $content = file_get_contents($file); if(!mb_check_encoding($content, 'UTF-8')){ $content = mb_convert_encoding($content, 'UTF-8', 'ISO-8859-15'); } echo $content; } } } /* * Renders the page title to the output. Is ment to be used in the template's * <title> tag. title() is language aware. */ function title(){ global $lang; global $path; global $current_node; if(file_exists(file_name_from_path($path))){ if('en' == $lang && !is_null($current_node->display_name_en)){ echo $current_node->display_name_en; } else { echo $current_node->display_name; } } else { if('en' == $lang){ echo "404 Page not found"; } else { echo "404 Seite nicht gefunden"; } } } /* * Renders the date of last modification of the current document to the output. * It uses the filesystem `mtime'. last_modified() is language aware. */ function last_modified(){ global $path; global $lang; $file = file_name_from_path($path); if(file_exists($file)){ $mtime = date("d.m.Y", filemtime($file)); if('en' == $lang){ echo("Last modified:&nbsp;$mtime"); } else { echo("Letzte &Auml;ndergung:&nbsp;$mtime"); } } } /* * this renders the quicklings section of the page. Mainly a convenience alias * to `load_template()'. */ function quicklinks(){ load_template('quicklinks'); } /* * renders the infloline/footer section of the page. Mainly a convenience alias * for `load_template()' */ function footer(){ load_template('footer'); } ?> <?php /* * main function that sets up all relevant global variables and calls the data * model creation and content rendering functions */ $path = 'index.html'; if(isset($_GET['lang']) && 'en' == $_GET['lang']){ $lang = 'en'; } else { $lang = 'de'; } /* * Check if the requested file is a text file. If so, try to serve it in * the cms. Otherwise (e.g. an image) just serve it as is. */ if(file_exists(file_name_from_path($path))){ $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime_type = finfo_file($finfo, $path); finfo_close($finfo); if(!is_dir($path) && !preg_match("/^text\//", $mime_type)){ header("Content-Type: $mime_type"); echo(file_get_contents($path)); exit(0); } } else { header("HTTP/1.1 404 Not Found"); } $config = load_config('mth-cms/config.ini'); $base_node = new Node('.'); /* * This tries to set the $current_node to the Node object corresponding to the * current $path. This might fail if the actual file is on a black list and * thus not added to the $base_node tree. If this is the case, we just use the * Node of the parent directory. This seems to be the best solution because * file name black listing is mostly used to exclude `index' files. */ $current_node = $base_node->find_node_by_path($path); if(is_null($current_node) && in_array( pathinfo($path)['filename'], $config['cms']['file_name_black_list'] ) ){ $current_node = $base_node->find_node_by_path(pathinfo($path)['dirname']); } // debugging //echo '<pre>'; //print_r($_GET); //print_r($base_node); //print_r($_SERVER); //print_r($current_node); //var_dump($config); //echo '</pre>'; load_template('main'); ?>
based on D8BkI
Output for 5.4.0 - 5.6.28, 7.0.0 - 7.1.0
Warning: Cannot modify header information - headers already sent by (output started at /in/ukL48:670) in /in/ukL48 on line 698 Warning: parse_ini_file(mth-cms/config.ini): failed to open stream: No such file or directory in /in/ukL48 on line 16 Warning: include(/home/ps0ke/Projects/Greiner/global_root_dir/main.html): failed to open stream: No such file or directory in /in/ukL48 on line 560 Warning: include(): Failed opening '/home/ps0ke/Projects/Greiner/global_root_dir/main.html' for inclusion (include_path='.:') in /in/ukL48 on line 560
Output for hhvm-3.12.0
Warning: file_exists() expects parameter 1 to be string, null given in /in/ukL48 on line 688 Warning: Cannot modify header information - headers already sent in /in/ukL48 on line 698 Warning: No such file or directory in /in/ukL48 on line 16 Warning: File not found: /home/ps0ke/Projects/Greiner/global_root_dir/main.html in /in/ukL48 on line 560
Output for hhvm-3.10.0
Warning: Cannot modify header information - headers already sent in /in/ukL48 on line 698 Warning: No such file or directory in /in/ukL48 on line 16 Fatal error: Uncaught exception 'Exception' with message '. does not exist.' in /in/ukL48:286 Stack trace: #0 /in/ukL48(703): Node->__construct() #1 {main}
Process exited with code 255.