3arcade PHP game script updated to version 2.1 to work with PHP 5.6

I have an installation of the 3arcade script that’s still in the wild, and decided it was time to update it to work with PHP 5.6 (at least).

These changes fix:
– a row 0 error in play.php linked to deprecated functions
– deprecation of the ereg_replace function through use of intval
– deprecation of the mysql_connect function (and future deprecation of mysql* functions) through use of PDO
– consistency of database connection and query errors with a variable in the config file

Changes live at https://github.com/withdave/3arcade

DateTime with no DateTimeZone set in PHP.ini with ffmpeg and ClipBucket

I’ve been exploring how to generate videos on the fly through use of packages like ffmpeg (I installed this the other day), and recently tried out ClipBucket (a free video site script).

ClipBucket is a little rough around the edges, but has a load of great features, has a relatively active community, and large parts of the code are on GitHub.

It’s easy to setup, but I wasn’t getting thumbnails through ffmpeg for any of the uploaded videos. Instead I was getting:

Response : Oops ! Not Found.. See log

Command : /usr/bin/ffmpeg -ss -i /var/www/html/files/...

Invalid duration specification for ss: -i

Searching the web returned no results, so I’m posting this as a record.

 

The Culprit

The culprit was the casting of time, which is used when a duration is available.

It uses the following line in ffmpeg.class.php:

$d = new DateTime($d_formatted);

And on my server, this threw an invisible exception:

DateTime::__construct(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone.

The DateTime construct gets rather unhappy when it doesn’t have a default timezone, and it turns out I’d neglected to put a default timezone in PHP.ini. Just a quick change in PHP.ini to:

date.timezone = Europe/London

Restart Apache and we’re good to go. Oops.

Another option for this is to set the timezone in the PHP code, but it should still be set at the server level:

$d = new DateTime($d_formatted, new DateTimeZone('Europe/London'));

 

Google Maps Distance (DistanceMatrix) API for UK in JSON

The postcode code has been updated to use Google’s distancematrix api, which provides a very different set of data from the old “as the bird flies” calculation (it calculates road distance, and provides transport options etc).

The following code is merely a demonstration, which can be seen here.

<?php
// Specify Postcodes to Geocode
$postcode1 = 'BH151DA';
$postcode2 = 'BH213AP';

// Set and retrieve the query URL
$request = "http://maps.googleapis.com/maps/api/distancematrix/json?origins=" . $postcode1 . "&destinations=" . $postcode2 . "&mode=driving&language=en-GB&sensor=false&units=imperial";
$distdata = file_get_contents($request);

// Put the data into an array for easy access
$distances = json_decode($distdata, true);

// Do some error checking, first for if the response is ok
$status = $distances["status"];
$row_status = $distances["rows"][0]["elements"][0]["status"];

if ($status == "OK" && $row_status == "OK") {

// Calculate the distance in miles
$distance = $distances["rows"][0]["elements"][0]["distance"]["value"];
$distance_miles = round($distance * 0.621371192/1000, 2);

echo 'The distance between '.$postcode1.' and '.$postcode2.' is '.($distance/1000).'Km ('.$distance_miles.' miles).';

} else {
    echo "Calculating the distance between your locations caused an error.";
}

?>

Having better error checking would also be a good idea if you plan to use the above code. Using &unit=imperial is optional, as Google always returns metres – so the code runs a basic calculation on these to convert to miles.

Facebook Like Buttons, &fb_xd_fragment=, Blank pages and SEO

This is a problem that’s been around for months now and yet still bugs thousands of users.

Any of your pages with facebook widgets on can result in URL’s like

www.example.com/page.php?fb_xd_fragment=

This has several implications. Firstly – it can cause blank pages through unwanted interactions with div areas on the page. We can solve this by adding the following fix just before the </body> tag. This came from http://forum.developers.facebook.net/viewtopic.php?id=60571&p=1 (temporary solutions)

<!-- Correct fb_xd_fragment Bug Start -->
<script>
document.getElementsByTagName('html')[0].style.display='block';
</script>
<!-- Correct fb_xd_fragment Bug End -->

The second, more long term issue is that this page will appear in search results alongside the normal page…resulting in duplicate content. Obviously you could just remove the like button but that’s not an ideal solution. So you can do a couple of things.

Head to webmastertools (https://www.google.com/webmasters/tools/home) and add the fb_xd_fragment= as something that should be ignored on your site.

Filtering out fb_xd_fragment in Google Webmaster Tools
Filtering out fb_xd_fragment in Google Webmaster Tools

Another option is to use .htaccess and 301 redirects to clip out the &fb_xd_fragment=, which is a pain but very easily do-able and removes the requirement to put the display fix on every page. So try this (modified per your site) in your .htaccess.

RewriteCond %{QUERY_STRING} fb_xd_fragment=
RewriteRule ^(.*) http://www.example.com/$1? [R=301]

Note: This is now fixed.

phpBB 3 Script Integration – New Threads and Replies from an External Script

This is the third article I’ve written for pulling functions in phpBB3 for external use. This one allows you to either create new posts in a forum or reply to a thread. This was created for use with a text system, where users could text in comments which would be added to a thread.

Article on phpBB3 Integration
Article on sending PM’s

I’ve put everything needed into one file as I don’t want to go through and break it up. The previous two posts (linked above) used a seperate phpbb.php file with part of the code in but this just includes everything.

<?php
// This is not set up for new thread posting, and has been config'd to not increment post count as this is for a bot.
// Further changes will be needed to clean up code as this is using external functions instead of clear documentation. --dc
define('IN_PHPBB', true);
$phpbb_root_path = (defined('PHPBB_ROOT_PATH')) ? PHPBB_ROOT_PATH : './phpBB3/';
$phpEx = substr(strrchr(__FILE__, '.'), 1);
include($phpbb_root_path . 'common.' . $phpEx);

// Start session management
$user->session_begin();
$auth->acl($user->data);
$user->setup();

// post send controller
function sendphpbbpost($pmmessage,$userid,$pmsubject) {

include_once('phpBB3/includes/functions_posting.php');
$my_subject = utf8_normalize_nfc(request_var('$pmsubject', '', true));
$message = utf8_normalize_nfc($pmmessage, '', true);
$uid = $bitfield = $options = '';
$allow_bbcode = $allow_smilies = true;
$allow_urls = true;
generate_text_for_storage($message, $uid, $bitfield, $options, $allow_bbcode, $allow_urls, $allow_smilies);
$data = array(
// General Posting Settings
'forum_id'          => 7,    // The forum ID in which the post will be placed. (int)
'topic_id'          => 5,    // Post a new topic or in an existing one? Set to 0 to create a new one, if not, specify your topic ID here instead.
'icon_id'           => false,    // The Icon ID in which the post will be displayed with on the viewforum, set to false for icon_id. (int)

// Defining Post Options
'enable_bbcode' => false, // Enable BBcode in this post. (bool)
'enable_smilies'    => true, // Enabe smilies in this post. (bool)
'enable_urls'       => false, // Enable self-parsing URL links in this post. (bool)
'enable_sig'        => true, // Enable the signature of the poster to be displayed in the post. (bool)

// Message Body
'message'           => $message,     // Your text you wish to have submitted. It should pass through generate_text_for_storage() before this. (string)
'message_md5'   => md5($message),    // The md5 hash of your message

// Values from generate_text_for_storage()
'bbcode_bitfield'   => $bitfield,    // Value created from the generate_text_for_storage() function.
'bbcode_uid'        => $uid,     // Value created from the generate_text_for_storage() function.

// Other Options
'post_edit_locked'  => 0,
'topic_title'       => $subject, // Subject/Title of the topic. (string). This is needed for new posts but for our purposes isn't.

// Email Notification Settings
'notify_set'        => false,        // (bool)
'notify'            => false,        // (bool)
'post_time'         => 0,        // Set a specific time, use 0 to let submit_post() take care of getting the proper time (int)
'forum_name'        => '',       // For identifying the name of the forum in a notification email. (string)

// Indexing
'enable_indexing'   => true,
'force_approved_state' => true, // Only runs on 6 onwards...check SUCC
);

//Now send that post...
submit_post('reply', '', '', POST_NORMAL, &$poll, &$data, $update_message = true);

}

$user->data['user_id'] = 2;    // User id of poster.
$auth->acl($user->data); // Re-initiate user after recall
$userid = $user->data['user_id'];
$pmmessage = 'This is a new reply, change this to whatever you want.';
sendphpbbpost($pmmessage,$userid,$pmsubject);

?>

Looking through the code above you will see several points that need changing. It’s currently set to ‘reply’ (change this to ‘post’ for a new post). Change all details to suit really – it’s easy to customise.

phpBB 3 Script Integration – Sending Private Messages (PM’s) From an External Script

A while back I posted an article about integrating your site to use the phpBB3 user and login system (here). When I wrote that I also used phpBB as a message system for a site; so when a user did something they were sent a private message automatically.

This bit of code (use it in conjunction with phpbb.php mentioned in the post linked to above) allows you to call a function to send a PM to any user as long as you have their ID.

<?php
// PM send controller
include_once("phpbb.php");

function sendphpbbpm($pmmessage,$userid,$pmsubject) {
include_once('forum/includes/functions_privmsgs.php');

$message = utf8_normalize_nfc($pmmessage);
$uid = $bitfield = $options = ''; // will be modified by generate_text_for_storage
$allow_bbcode = $allow_smilies = true;
$allow_urls = true;
generate_text_for_storage($message, $uid, $bitfield, $options, $allow_bbcode, $allow_urls, $allow_smilies);
$pm_data = array(
'from_user_id'        => 2,
'from_user_ip'        => "127.0.0.1",
'from_username'        => "admin",
'enable_sig'            => false,
'enable_bbcode'        => true,
'enable_smilies'        => true,
'enable_urls'        => false,
'icon_id'            => 0,
'bbcode_bitfield'    => $bitfield,
'bbcode_uid'         => $uid,
'message'            => $message,
'address_list'        => array('u' => array($userid => 'to')),
);

//Now We Have All Data Lets Send The PM!!
submit_pm('post', $pmsubject, $pm_data, false, false);
}

?>

You’ll need to modify the code above slightly to fit your needs, for example changing the admin username and user id, and then calling the function but it’s all pretty simple stuff one you’ve got all this.

To call the function (send the PM) to a fictional user 11:

<?php

$pmsubject = 'Please read this PM.';
$pmmessage = 'Thank you for reading this PM!';

$userid = '11';

sendphpbbpm($pmmessage,$userid,$pmsubject);

?>

Posting threads and replying to threads uses a similar system so I’ll put up some script for that shortly.

Benchmarking and testing your PHP script with microtime

When you’re building a new website you often code it ‘on-the-fly’. That’s to say, strapping in new features here and there until it does exactly what you want – but leaving a mess that needs to be optimised.

One of the best ways of testing your site for scalability (other than getting huge traffic straight away) is to test how long your PHP scripts take to parse. We can do this by comparing the time at the start and end of a script to give us the processing time.

Continue reading “Benchmarking and testing your PHP script with microtime”

Using Google Maps, Geocoding and PHP to find the distance between UK Postcodes

UPDATE: A new version which calculates distance based on roads is available.

If you’re looking to make any kind of radius checker, delivery calculator etc, you will need to have some method of calculating this distance. Unfortunately for us in the UK, Royal Mail keep a tight grip on postcode data.

As a result, the best low-budget way of finding postcodes is by using the Google Maps api – which in itself isn’t 100% accurate (but good enough).

So we can use the following code:

<?php
// Specify Postcodes to Geocode
$postcode1 = 'BH151DA';
$postcode2 = 'BH213AP';

// Geocode Postcodes & Get Co-ordinates 1st Postcode
$pc1 = 'http://maps.google.com/maps/geo?q='.$postcode1.',+UK&output=csv&sensor=false';
$data1 = @file_get_contents($pc1);
$result1 = explode(",", $data1);
$custlat1 = $result1[2];
$custlong1 = $result1[3];

// Geocode Postcodes & Get Co-ordinates 2nd Postcode
$pc2 = 'http://maps.google.com/maps/geo?q='.$postcode2.',+UK&output=csv&sensor=false';
$data2 = @file_get_contents($pc2);
$result2 = explode(",", $data2);
$custlat2 = $result2[2];
$custlong2 = $result2[3];

// Work out the distance!
$pi80 = M_PI / 180;
$custlat1 *= $pi80;
$custlong1 *= $pi80;
$custlat2 *= $pi80;
$custlong2 *= $pi80;

$r = 6372.797; // mean radius of Earth in km
$dlat = $custlat2 - $custlat1;
$dlng = $custlong2 - $custlong1;
$a = sin($dlat / 2) * sin($dlat / 2) + cos($custlat1) * cos($custlat2) * sin($dlng / 2) * sin($dlng / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));

// Distance in KM
$km = round($r * $c, 2);

// Distance in Miles
$miles = round($km * 0.621371192, 2);

echo 'The distance between '.$postcode1.' and '.$postcode2.' is '.$km.'Km ('.$miles.' miles).';

?>

You could use $result1[0] and $result2[0] to check codes. If the value is anything other than 200 the postcode is invalid. Also note UK is also searched for to guarantee correct results!

The result is also rounded to make sure we only have 2 decimal places. Make sure your postcodes do not have any spaces in when they go to Google, if you’re collecting them from a form maybe use:

function nowhitespace($data) {
return preg_replace('/\s/', '', $data);
}
$postcode1 = nowhitespace($postcode1);

to remove all spaces before processing, and the following to check it’s ok after processing:

if (($result1[0] != 200) || ($result2[0] != 200)) {
echo "<p>Invalid Postcode(s) Entered. Please try again.</p>";
} else {

Good luck!

Validating email addresses in PHP with Preg filters, DNS, MX Servers and other checks

UPDATE: It is now easier to send a confirmation email to the provided address to validate it, or to use a framework than a custom script.

There are many tutorials online that show users how to validate an email address, but most do it wrong. This means many websites will reject valid addresses such as customer/[email protected] or abc!def!xyz%[email protected] (yes, they are valid!) with the following expression:

Continue reading “Validating email addresses in PHP with Preg filters, DNS, MX Servers and other checks”