Blog app #1: the basics

Published on Monday 25 April 2016. Tagged as PHP.

Using the .htacces file to prevent directory listings, redirecting all requests to one .php file and setting expiration dates. Launching the blog app or its admin interface from the index.php file.

The sample code is now on Github: michelvdm/blog-app. The app is intended as a learning device to code web applications using plain PHP, HTML and CSS and vanilla JavaScript. I deliberately used no frameworks to build it.

How to install

To test, you should have a local web server with PHP and MySQL installed, e.g. XAMPP. You can then grab the files from Github and copy them in the htdocs folder of the server. Open the .../extra/how-to-install.php file on the local server in your browser for the next steps.

The main .htaccess file

Directly in the top directory of the app, the .htaccess file prevents directory listing, rewrites the URLs so that all the requests are redirected to the index.php file. The last block of the file adds expiration dates to files for browser caching.

Options -Indexes

Prevents directory listing.

RewriteEngine On
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteRule ^(.+)$ ./index.php?url=$1

Redirects all requests that do not point to existing files to index.php?url=....

<IfModule mod_expires.c>
ExpiresActive On 
ExpiresDefault "access plus 1 hour"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
ExpiresByType application/javascript "access plus 1 year"
IfModule>

Enables browser caching by setting expiration dates to files according to their content type.

The main index.php file

The main index.php file starts with the definition of a few constants:

define( 'START_TIME', microtime( true ) );
define( 'BASE', getcwd() );
define( 'ROOT', dirname( $_SERVER[ 'PHP_SELF' ] )=='\\'?'':dirname( $_SERVER[ 'PHP_SELF' ] ) );

Next a few small utility functions:

function out($val){ echo $val, PHP_EOL; }
function debug( $val, $label='Debug' ){require(BASE.'/sys/debug.php');}

Next are the debug settings (rem out one of the two lines). Full error reporting should be enabled locally for testing, but all error reporting should be off in a production environment:

//ini_set( 'display_errors', 0 ); error_reporting( 0 );
ini_set( 'display_errors', 1 ); error_reporting( E_ALL );

The following code enables pre-loading all classes. This method is faster than loading them manually using require(...) just before using them for the first time. Note: all classes I use are located in the sys/classes/ folder.

spl_autoload_register( function( $class, $data=null ){ require_once( str_replace( '\\', '/', BASE.'/sys/classes/'.strtolower( $class ).'.php' ) ); });

Finally, the following three lines creates the actual application class (the controller) and execute it's handleRequest method:

$config=require( BASE.'/content/config.php' );
$app=( 'http'.(isset($_SERVER['HTTPS'])?'s':'').'://'.$_SERVER[ 'HTTP_HOST' ]==$config[ 'adminUrl' ] )?'AdminController':'BlogController'; 
call_user_func( array( new $app( $config ), 'handleRequest' ) );

Next: in following posts, I'll continue documenting the code, starting with the BlogController class, the binding class for the MVC application.

The Blog app project on GitHub