Show PHP source without duplicating files

Every once in a while I want to show a php script’s source code. I would always have a .php file to show an active file in use and a companion .phps file (php-source) to show the source. With a little mod_rewrite and php this can be done with just the one active file, and two supporting files that never change.

The original setup was just to use mod_rewrite to map any .phps request that didn’t find a real file to a .php variant, if it existed, and remap the .php file’s mime type to that of a .phps file. However, that didn’t work for me. I think php might have been grabbing and processing the file before it could be remapped and delivered.

The trick was to route the request to another file that took the requested file as a passed variable and then have it check for the file and show it. Here’s how it works:

The .htaccess file

This the core to making the system work. It grabs any request for file.phps where file.phps does not exist and routes it to source.php?f=file.php.

OptionsFollowSymLinks
RewriteEngineon

#donotrunonrealfiles
RewriteCond%{REQUEST_FILENAME}!-f
RewriteCond%{REQUEST_FILENAME}!-d

#mapphpstophp
RewriteRule^(.+.php)s$source.php?f=$1

The PHP file

<?php
	/**
	 * PHPS source file viewer
	 *
	 * @author: Shawn Parker, http://www.top-frog.com
	 * 			http://www.top-frog.com/archives/2006/11/22/show_php_source_without_duplicating_files
	 *			This file has been modified slightly since the article was written
	 *
	 * @requires: http://pear.php.net/package/Text_Highlighter/docs
	 *
	 * @example: put this file and an htaccess file with the information outlined below in a
	 * dedicated directory. Add in any other files that you'd like to view the source for and
	 * call them with an extra s in the name. So, for example myscript.php can be viewed at myscript.phps
	 *
	 * .htaccess requirement
	 *
	 * include the following items in the .htaccess file in
	 * the directory you'd like to use

		Options FollowSymLinks

		RewriteEngine on
		RewriteRule (.*) source.php

	*
	*/

	$types = array(
		'js' => 'JAVASCRIPT',
		'php' => 'PHP',
		'phps' => 'PHP',
		'css' => 'CSS',
		'xml' => 'XML',
		'sql' => 'SQL',
		'py' => 'PYTHON',
		'htm' => 'HTML',
		'html' => 'HTML',
		'shtml' => 'HTML',
		'rb' => 'RUBY',
		'pl' => 'PERL',
		'sh' => 'SQL' // not a great solution, but it looks the best
	);

	$filename = str_replace('phps','php',array_pop(explode('/',$_SERVER['REQUEST_URI'])));
	$file = dirname(realpath(__FILE__)).($filename != 'source.php' ? '/files/' : '/').$filename;
	if(is_file($file)) {
		require_once('Text/Highlighter.php');
		$th =& Text_Highlighter::factory($types[pathinfo($file,PATHINFO_EXTENSION)],array('numbers' => HL_NUMBERS_LI));
		$src = $th->highlight(file_get_contents($file));
	}
	else {
		$src = '<p>I could not find <i>'.$file.'</i> in the allowed directory structure. '.
			   'Please check your request and try again. If you followed a link here then please '.
			   'contact that system administrator and inform him/her of the bad link.</p>';
		$file = 'File not found';
	}

?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
	<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
	<title>Source for: <?php echo $file; ?></title>
	<link rel="stylesheet" type="text/css" href="highlight.css" />
	<style type="text/css" media="all">
		body{margin:30px;}
		h1,#footer{background:#ddd;}
		h1{font-family:arial,helvetica,sans-serif;font-size:18px;border-bottom:1px solid gray;padding:10px;}
		#footer{padding:5px;border-top:1px solid gray;font-size:11px;margin-top:15px;}
		#footer p{margin:0;padding:0;}
		ol.hl-main{background:#efefef;}
		ol.hl-main li{white-space:pre;font:10px/1.3 sans-serif;background:white;margin-bottom:1px;padding:1px;}
		ol.hl-main li span{font:12px/1.3 courier,"courier new",monospaced;}
	</style>
</head>
<body>
	<h1>Source code for <?php echo $file;  ?></h1>
	<div id="source">
<?php echo $src ?>
	</div>
	<div id="footer"><p>Page delivered by: <a href="source.phps">PHPS source file viewer</a>.</p></div>
</body>
</html>

What it does it take the passed variable via $_GET and if its a file it uses the built in php function highlight_file to do the source coloring. The source is then dropped into a mildly formatted html template.

Security Concerns

There are many things to consider with this script. For one, I don’t scrub the $_GET var.

My best assertion of keeping it secure is to keep this limited to the directory that you’re showing files from. If you do enough validation and checking you can have it go directly to source files in your site, but going this route requires you to be very sure you do the proper exclusions so that people can’t grab any config or password files.

Right now this file will show itself – this is a security concern if you do allow it to dig through your actual source files since it then reveals all or part of your source structure.

Since I would mainly use this for showing off conceptual files I have no problem with it being a “sandbox” and limit its access to the system.

The End

There it is, nothing revolutionary, very straight forward, and very useful if you like to share code ideas with others. The script is terribly generic and will work with a base install of PHP4 or PHP5. As usual, if you make this better or find any bugs, please let me know.

Tagged as: htaccess mod_rewrite php php-source phps source view