The working directory in PHP

A colleague recently came to me with this issue that worked fine in their browser, but not on the command line:

Warning: include(../config.php): failed to open stream: No such file or directory

This warning appeared because of the current working directory.

When accessing this script via the browser, Apache found the correct path to the file and so ‘../’ was relative to it’s own directory.

However by running it from a command line…

php /var/www/public/index.php

The current working directory was actually wherever the user is in the file system of the server.

In which case they could do:

cd /var/www/public/
php index.php

Better yet, they could use the __DIR__ magic constant in PHP which returns the directory of the file itself.

<?php
include(__DIR__ . '/../config.php');

If you ever need to figure out the current working directory, you can use the nifty function getcwd().

Google Maps – dragging a marker to new position

Something that took me a while to figure out was how to drag a marker on a map to a new position and then retrieve it’s lat/lng (version 3).

This assumes you have the map variable set as an instance of google.maps.Map and latlng which is the current position to show on the map as an instance of google.maps.LatLng.

var marker = new google.maps.Marker({
 map: map,
 draggable: true,
 position: latlng
});

google.maps.event.addListener(marker, 'dragend', function(){
 var position = marker.getPosition();
 map.setCenter(position); // set the map center to it's new position
 alert(position.lat() + ',' + position.lng()); // here are the new lat/lng strings
});

Simple PHP caching improved

There are many tools around that will help cache your web pages to increase speed and performance (most notably Memcached) however these usually require installation on your web server, thus rendering them useless for people who use shared hosting and don’t have access to the server.

Using some simple PHP code we can quite effectively cache a webpage, useful for sites that are database driven and have a large amount of queries or high volumes of traffic.

First we need to setup the folder where the cached files are stored, making sure it has read/write permissions (CHMOD to 0777 – see your FTP client documentation on how to do this).

Next create a file called cache.php which contains the following code:

<?php

class Simple_Cache {
 
 // Number of seconds a page should remain cached for
 public $cache_expires = 3600;
 
 // Path to the cache folder
 public $cache_folder = "/home/usr/www/cache/";
 
 // Include query strings to make the cached page file unique
 public $include_query_strings = true;
 
 // The current cache file, this will get set when loaded
 private $cache_file = "";
 
 /**
  * Set the current cache file from the page URL
  */

 public function __construct() {
  $file = $_SERVER['REQUEST_URI'];
  if (!$this->include_query_strings && strpos($file, "?") !== false) {
   $qs = explode("?", $file);
   $file = $qs[0];
  }
 
  $this->cache_file = $this->cache_folder . md5($file) . ".html";
 }
 
 /**
  * Checks whether the page has been cached or not
  * @return boolean
  */

 public function is_cached() {
  $modified = (file_exists($this->cache_file)) ? filemtime($this->cache_file) : 0;
  return ((time() - $this->cache_expires) < $modified);
 }
 
 /**
  * Reads from the cache file
  * @return string the file contents
  */

 public function read_cache() {
  return file_get_contents($this->cache_file);
 }
 
 /**
  * Writes to the cache file
  * @param string $contents the contents
  * @return boolean
  */

 public function write_cache($contents) {
  return file_put_contents($this->cache_file, $contents);
 }
}

// Initiate the cache class
$cache = new Simple_Cache();

// Check if the page has already been cached and not expired
// If true then we output the cached file contents and finish
if ($cache->is_cached()) {
 echo $cache->read_cache();
 exit();
}

// Ok so the page needs to be cached
// Turn on output buffering
ob_start();

Now in your page you would include cache.php on the first line before you do anything else. This is because if the page is cached we don’t want it to do anything else other than output the cache to the page. If you are still including other files or connecting to databases before checking for the cache you are putting additional load on the server, making this exercise pointless.

Finally create a file called cache_footer.php which contains the following code:

<?php
// Grab the uncached page contents
$cache_contents = ob_get_contents();
ob_end_flush();

// Save it to the cache for next time
$cache->write_cache($cache_contents);
?>

You would include this file on the last line of your page, which saves the page in the cache if required.

Putting it altogether a typical page would look like this:

<?php
// Load the cache process
include("cache.php");

// Connect to database
include("config.php");
mysql_connect($db_host, $db_username, $db_password) or die(mysql_error());
mysql_select_db($db_name) or die(mysql_error());
?>
<html>
<body>
<h1>Articles</h1>
<ul>
<?php
// Some query
$query = mysql_query("SELECT * FROM articles ORDER BY id");
while ($row = mysql_fetch_array($query)) {
  echo '<li><a href="view_article.php?id='.$row['id'].'">'.$row['title'].'</a></li>';
}
?>
</ul>
</body>
</html>
<?php
// Save the cache
include("cache_footer.php");
?>

Flintstone – A key/value database store using flat files for PHP

XEWeb are proud to announce the release of Flintstone, a key/value database store using flat files for PHP.

Some of the features include:

  • Memory efficient
  • File locking
  • Caching
  • Gzip compression
  • Easy to use

All it requires is PHP 5 and read/write file permissions.

Take a look at example usage below:

try {
 
    // Load flintstone
    $db = new Flintstone(array('dir' => '/path/to/database/dir/'));
     
    // Set keys
    $db->load('users')->set('bob', array('email' => 'bob@site.com', 'password' => '123456'));
    $db->load('users')->set('joe', array('email' => 'joe@site.com', 'password' => 'test'));
    $db->load('settings')->set('site_offline', 1);
    $db->load('settings')->set('site_back', '3 days');
     
    // Retrieve keys
    $user = $db->load('users')->get('bob');
    echo 'Bob, your email is ' . $user['email'];
     
    $offline = $db->load('settings')->get('site_offline');
    if ($offline == 1) {
        echo 'Sorry, the website is offline<br>';
        echo 'We will be back in ' . $db->load('settings')->get('site_back');
    }
     
    // Delete a key
    $db->load('users')->delete('joe');
     
    // Flush database
    $db->load('users')->flush();
}
catch (Exception $e) {
    echo 'Exception: ' . $e->getMessage();
}

For more information and to download visit http://www.xeweb.net/flintstone/

Generate a random string A-Z, 0-9 in PHP

Often I find that generating a random string can be tedious, there seem to be so many ways to do this.

I wrote this simple function to do the work, and it can be easily modified.

/*
 * Create a random string
 * @author XEWeb <http://www.xeweb.net>
 * @param $length the length of the string to create
 * @return $str the string
 */

function randomString($length = 6) {
 $str = "";
 $characters = array_merge(range('A','Z'), range('a','z'), range('0','9'));
 $max = count($characters) - 1;
 for ($i = 0; $i < $length; $i++) {
  $rand = mt_rand(0, $max);
  $str .= $characters[$rand];
 }
 return $str;
}

You can remove any of the ranges in $characters (for example if you didn’t want uppercase letters delete the range('A','Z')) or put in your own array of characters.