PHP include paths

June 9th, 2020

One of the nice things about PHP is that it is easy to write one common file for things like headers and footers and then include that file in every page that you publish. For simple sites, it is easy, you just include a line like this in as the first line your code.

require_once(''); ?>

where the file is something like this:

<!doctype html>
<html lang="en-US">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="copyright" content="Copyright ©<?php echo $now; ?> Well" />
  <meta name="rating" content="general" />
  <meta name="robots" content="all" />
  <meta name="description" lang="en" content="<?php echo $meta_description; ?>"/>
  <meta name="keywords" content="<?php echo $meta_keywords; ?>" />
  <meta name="robots" lang="en" content="index follow" />

    <link rel="icon" type="image/png" href="./common_images/favicon.png" />
    <link rel="apple-touch-icon" href="./common_images/apple-touch-icon.png"/>
    <link rel="stylesheet" href="/css/pure-min.css">
    <title><?php echo $page_title; ?></title>

Once you have the basic file in place, you can start embellishing it. Note that I refer to several PHP variables. If you examine the source for this page, you see that it is copied literally into the HTML source and a few variables that I previously defined take on their values. These are defined in a file that I call

    $day = date("Y-m-d",strtotime("now"));
    $today = date("Y-m-d");
    $nowDate = date('Y');    // Used for copyright notices in the header tag and in the footer.

  // Here is where you provide information about your site.
  $site_name = "Well Golly";
  $site_shortName = "Well Golly";
  $site_URL = "https://Well";

  $copyright_text = "© Copyright $nowDate $site_name";
  $footer_text = "&copy; Copyright $nowDate <a href=\"{$site_URL}\">$site_name</a> $site_address&nbsp;&nbsp;$site_phone";

  $keywords = "$site_name"; // Separate terms with commas
  $description = "$site_name web-based exercises.";

  $meta_description = 'Well Golly Exercises.';
    $meta_keywords = 'products, apps, software';

  $page_title = "$site_name Exercises";


Note that this file starts with because none of the information on the page is HTML. This file defines variables that I use in other files to generate HTML.

One thing that is confusing at first is how PHP finds the included files. If you just use the file name in the call, then PHP looks in the current directory for the file. PHP uses Unix conventions, so if you are in a subdirectory and want to call a file one level above, you would preface the file name with ../ e.g.

require_once('../'); ?>

Likewise, two levels up is ../../ and two levels up then down a level is ../../directory. This can get confusing after a while so what most people do is create a directory in the document root and put include files that are used throughout the site in it. Here’s an example of my include.php file for a site.

Remembering to put the correct number of ../s can be tiresome so what I do is define a search path and put it in my php.ini file. I don’t remember precisely what it looks like, but this line from my /var/log/php_error.log file tells me where PHP was looking for a file that it can’t find.

 PHP Fatal error:  require_once(): Failed opening required './include.php/' (include_path='.:/usr/share/php5:/usr/share/php:./include.php:..:../include.php:../../include.php:../../../include.php')

I basically says to look first in the current directory . , then in the PHP provided directories, then in the next level up, etc. If you want to override the search path you can just specify where PHP should look for the file. For example, in the page where users interact with your app, you may not want all of the normal branding and menu choices. Create a simple header file and access it directly by specifying the path.

require_once('./'); ?>

You can do the same thing for CSS and javascript. I do this for all of my apps since there is some common javascript but much of it is specific to an app and I don”t want the code to get confused because I used a function name that has a different input and output in other apps. Plus there is less code being downloaded so the page loads faster.

Where it gets tricky is when you start thinking that PHP starts looking in the document root for files and images when in fact it starts looking in the root of the server as defined in your Apache2 conf file. In my case it is /srv/www.

So if you want to include an image in your logo, you might put it in /common_images in the document root. However, if you call it using <img src='/common_images/logo.png' alt=logo' /> PHP won’t find it because it will be looking in /srv/www//common_images/logo.png. Remove the first slash and you are good to go—if you are calling the images from the same level as common_images. You could add the path to your php.ini file, but I have a bunch of folders that I use where I would have to do that. An easier way is to use PHP server variables. In the example below, I have already defined the $site_URL in my file and use a built-in PHP function, explode, to get the filename of the directory where my common_images directory is located. So no matter how far down the directory tree I am, I can still get the location where the logo is located—

<div id="header" class="pure-g">
  <div class="pure-u-1 pure-u-lg-1-2">
    <a href="<?php echo $site_URL ?>">
      $curRoot = explode("/", $_SERVER['REQUEST_URI']);
      <img class="pure-img-responsive" src="<?php echo "{$site_URL}/{$curRoot[1]}" ?>/common_images/wg_header.png" alt="Header">

Finding paths with PHP

June 9th, 2020

I’m worling on web apps and need to do two things that require knowing where you are in the file hierarchy. When a user clicks on an app that I am charging for, I need them to log in. But when they are done with the login, I want to send them back to the app that they want to use. So what I do is use the server variable PHP_SELF to get the complete URL of the file they clicked on. I need to make sure that there is a active session and then add a session variable, calledFileName. Once the user has finished the login or signup process, I redirect them to the app they want to use.

// if user is not logged in, restrict what they can do
$calledFileName = $_SERVER['PHP_SELF'];
$_SESSION['calledFileName'] = $calledFileName;

When they hit the submit button, I call a bunch of validation code and reload the form page. Before any HTML is sent, I check to see how they got to the login/signup page and then send them to the appropriate page.

$startlocation = $_SESSION['calledFileName'];
if ($passwordIsValid) {
  if ( strlen($startlocation) > 0 ) {
    header("Location: $startlocation");
  } else {
    header("Location: /exercises/overview.php");

Most of my web pages have been fairly simple so I the past I have done live editing to fix mistakes or update text. When I converted one of my sites to responsive HTML5, I created a beta subdomain, e.g., and did all the changes there and them rsynced the whole thing to the original site. That worked because I used hard coded paths and one global variable for the site domain name.

I have active users of the exercises and I don’t want to break things for them so live editing is out. I am developing on a beta subdomain, but I want to be extra careful that nothing breaks. I also want to make the site robust enough that I can easily move it to another domain if someone wants to license the apps. So rather than absolute paths, I’ve been using relative paths. This mostly works, but there are some instances where I want to use the same file in different locations and the images that file calls need a fixed path.

I have been using git to sync the files and it seems to work as well as rsync. Because I want to be extra careful about breaking the site for active users, I created a test folder for the exercises on the live site, exercises_test, and synched it with the beta content. Then renamed it when I was sure everything was working. The relative paths work fine but hard coded paths won’t work in this case since the files are not located in /exercises/folder but are located in /exercises_test/folder. One case where it doesn’t work is when I redirect the user to the login page if they haven’t yet logged in. In this case, I use another server variable and grab the root directory. The variable looks like this: /exercises_test/apptype/index.php so when I grab the root folder with explode, the first element of the array is empty, so I need to get the second element of the array.

if ($_SESSION['userID'] == '') {
  header("HTTP/1.1 302 Found");
  $curRoot = explode("/", $_SERVER['REQUEST_URI']);
    header("Location: /$curRoot[1]/login.php");

I can use this same trick for path names in my header files.

PHP password storage in MariaDB

June 2nd, 2020

Years ago I built a site that had login and account creation capability using SHA1 and MySQL. In preparation for a new webiste I loaded the old site into a droplet it still works fine. At the time SHA1 and simple input sanitizing was fine, but since then SHA1 has been cracked and PDO is recommended for sanitizing database values.

The original code looked like this:

// Read the data from the form
$userlogin = htmlspecialchars($_POST['login']);
$password  = htmlspecialchars($_POST['password'];
// The hash stored in the database.
$passwdHash = sha1($Password);

// Verification
$userlogin = htmlspecialchars($_POST['login']);
$password  = htmlspecialchars$_POST['password'];
$passwdHash = sha1($password);
$sql = "SELECT *
FROM users
WHERE username = '$login' && password = '$passwdHash' ";

Without really thinking about it, I sanitized the password input with htmlspecialchars and in both storing and retrieving the password it didn’t have any impact.

I basically copied the code into my new project and then changed SHA1 to password_hash. That didn’t work because the password_hash requires password_verify to check the hash when you retrieve it from the database. That worked when I tested the login code by copying and pasting the login info from php_error.log –> error_log(login: $userlogin  hash: $hash) into the users table. It didn’t work when I created a signup page and entered the data automatically. The reason is that sanitizing the password with htmlspecialchars changes it somehow. This is what I do instead. Note that it doesn’t matter what the user enters in the password field because the hash function transforms it into a 60 character hash so there is no need to sanitize.

// Read the data from the form
// Sanitized passord is used in javascript just for checking that a password was entered
$Password = htmlspecialchars($_POST["Password"]); 
$UserLogin = htmlspecialchars($_POST["UserLogin"]);
$PasswdHash = password_hash($_POST["Password"], PASSWORD_DEFAULT);

// Enter into the database with PDO
$sql = "INSERT INTO `Database`.`users` (`user_login`, `user_pass`,
$sql .=  "VALUES (:user_login, user_pass, ...

$stmt->bindParam(': user_login', $UserLogin);
$stmt->bindParam(':user_pass',   $PasswdHash);

// Validate the password. Retrieve by user_login only. 
// Don’t do anything to the password that was entered by the user.

foreach($result as $row) {
  $user_login  = $row['user_login'];
  $hash = $row['user_pass'];

  if ( password_verify($_POST['password'], $hash) ) {
    $_SESSION['userID']    = $row['ID'];
    $_SESSION['userName']   = $row['user_name'];
    $_SESSION['userDisplayName'] = $row['display_name'];
    $passwordIsValid = TRUE;

Find text of one column in another

May 20th, 2020

We’re writing a game where the target word must be in the phrases and sentences. To make sure that this happens, I used this code to find the targets not in the phrase.

SELECT * FROM `ArticIV_ShowMe` WHERE !(`phrase`  LIKE CONCAT('%', `target` ,'%'))

So you think you want a website.

May 15th, 2020

I have been helping some people set up websites recently and thought it would be good to explain the basic elements to putting up a website.

Domain registration.
This is done by GoDaddy, Namecheap, etc. It is where you pick your domain name. You can have multiple domain names for your site. That’s especially useful if your company has a long name, but you want an easier to remember website name. So if your name, is Old Towne Bike Shop and Brew, you might want to also register BikeAndBrew, Note that domain names are not case sensitive, so you can use caps like to make them more readable. Be careful when you pick you name so that there isn’t something bad in the middle. My brother-in-law got a license plate for his Wild Horse Ranch but could only use 8 letters. WILDHORS just doesn’t cut it. Likewise, Dr Goode, Physical Therapist shouldn’t pick,

DNS – Domain Name Service
This is where you point the domain name to a specific IP address. Every device on the internet that communicates to the outside world has an IP address. The modem in your house has an IP address that is assigned by your Internet Service Provider (ISP). When you visit a website, you send along your IP address. When the website responds, it sends information to your IP address and your router then sends it to the device in your house or office that requested the information. Same thing happens with your phone when you are out and about. The IP address can change but you don’t want your websites address to change.

So when you buy a domain you need to point it to a fixed IP address so the world knows how to find you. You get that from whoever provides your server. Then you tell the DNS where to find the IP associated with your domains. My server You can reach my server at this address and it sends you to the default site. There are many websites on my server and the webserver that I use (Apache2) sees that is being asked for and redirects traffic to a folder on the hard drive.

You can use your registrar for DNS but I use the server provider—Linode.

You can have GoDaddy host your website if you want. Lots of people I work with do that. Or you can have your own PC. I used to do that but now it is much cheaper to use the cloud. I share a machine with others but to me it looks like I have the machine all to myself. Some of my domains are at Linode and some are at Digital Ocean. Large companies use Amazon AWS, Google, or Microsoft. There are many many alternatives for all budgets and loads.

SSL Certificate
The lock button on the website means that traffic is encrypted from my server to your computer. It assures that the traffic is not messed with and no one knows what you were reading. All they know is that you visited a site. You used to have to pay a lot for SSL certificates but now you can get them from Cloudflare or Certbot for free. I use Certbot. This doesn’t verify that you are a legitimate company, just that the traffic is encrypted. Banks and big retailers use other services that also verify the identity of the company.

The actual website.
I use a combination of code and WordPress for my sites. The nice thing about WordPress is that I can set up people to use it and they can’t do anything else on the server.

You can get all five of these services as a bundle from lots of companies—GoDaddy, Squarespace, Wix, WordPress, etc.

WordPress site health
I am running the server on Ubuntu 18.04.4 LTS which means that I can update it regularly until 2023 and nothing will break. There is a new Long Term Service version that just became available and I am testing it on one of my servers. It updates PHP but I don’t need any new features so I haven’t updated it on this server yet.

On one of the sites that I host the user was concerned that the WordPress Health Check flagged imagick PHP module as a recommended performance improvement. Apparently one of the plug-ins that they are using for uploading images works better with it. I installed it from the command line using sudo apt-get install php-imagick.

It also noted that there were plug-ins that weren’t updated. I don’t have automatic updates enabled (I actually don’t know how to do that.) but you can update the plugins by looking for the red number next to the Plugins tab.

They also got a few Critical Issues. The REST API encountered and error and Could not complete loopback request. These errors are due to either a problem with a plug-in or conflict between plug-ins. If the site works, just ignore them. Otherwise, disable plug-ins until the error goes away and then figure out how to fix it.

Site Metrics
If you want to use Google to track the site, there are plug-ins that let you do it or you can copy the code and I can out it on the site. One benefit of using Google for tracking is that you know they have indexed your page.

You can Google how to do it, this page seems reasonable.

Matomo also has a plug-in that avoids having Google spy on all of your traffic.

Well Golly

Atheism Plus

Buy from Amazon