beachcoder.co.uk

30Aug/13Off

Late summer


Directions

Filed under: Downtime No Comments
21Apr/12Off

Using anonymous functions to build WordPress plugins

I work in WordPress a lot, so I invariably use plugins to add functionality. Sometimes, if you can't find exactly what you want, you have to roll your own.

Whenever I think "It'd be quick enough to knock up plugin to do that" I get the annoying feeling of having to rewrite the plugin starting point. Deciding on a unique namespace or function prefix, setting up the admin menus, hooking into content, the usual. It's not a huge job, but I hate writing things I've done before.

Yes, I could build a template with the common functions, but that'd still mean changing all th function names, admin menu hooks and so on. Still annoying.

So I put together a template that uses anonymous functions. All I have to do is change the name of the plugin in two places (in an $info array and the initial comment) and that's it. I have an admin hook, admin menu, options namespace with save & load - it's all good to go. Because it uses anonymous functions, there are no conflicts; I can copy & paste the file 10 times if I want, and each copy will activate as a separate plugin.

Here's the template:

/*
Plugin Name: Plugin base
Plugin URI: http://www.example.com/
Description: Default plugin
Version: 1.0
Author: David
Author URI: http://www.example.com/
*/

$plugin=function(){

	// Set some global details here

	// Plugin name
	$info=array('name'=>'Plugin base');

	// Options namespace (tied to filename)
	$info['ns']=md5(__FILE__);

	// Update options
	if($_POST[$info['ns']])update_option($info['ns'],$_POST[$info['ns']]);

	// Get latest options
	$info['opts']=get_option($info['ns']);

	// Define admin control panel
	$admin=function()use($info){
		echo $info['name'];
	};

	// Add to admin menu (must appear after function definition)
	add_options_page($info['name'],$info['name'],8,__FILE__,$admin);
};
if(function_exists('add_action'))add_action('admin_menu',$plugin);

$p contains all the internal functions. That's where the magic is; $p defines everything on the fly. If multiple plugins are created this way, they're executed one after the other. $p gets overwritten each time and there are no conflicts.

You can even define functions that don't conflict with anything by dropping them into an array:

$info['fn']['test_function']=function($string){
	return $string;
}

These will be available in the scope of the $p function and therefore available to all functions defined within. I won't lie - variable scope can get a bit messy, but as long as you're passing variables around you should be OK.

One disadvantage is that anonymous functions are only available in PHP 5.3. And I can't honestly argue that this is any better than using a class definition. Although I use this, it's partly because it was an interesting exercise to put together - I didn't write it for maintenance by anyone else :)

26Jun/11Off

Mobile redirects via .htaccess

Rather than have the overhead of your web app checking for mobile devices (or, God forbid, Javascript) .htaccess can provide a much neater solution. It's a single file, completely independent from the rest of your application, and runs on most webservers including IIS (with the right plugins).

This is a script I use that redirects mobile users from www.example.com over to m.example.com. It includes checks for iPads and Macs which would otherwise trigger as a mobile device, when in fact they should be served the full site by default.

There's a link on m.example.com back to the full site if the user wants it; this just needs a single GET variable like so: http://www.example.com?nomobile
That will set a cookie on the mobile device allowing it to skip the mobile checks and redirect.

RewriteEngine On
RewriteBase /
 
# Has not requested the full site
RewriteCond %{QUERY_STRING} !nomobile$ [NC,OR]
 
# Thre’s no ‘nomobile’ cookie
RewriteCond %{HTTP_COOKIE} !nomobile=1 [NC,OR]
 
RewriteCond %{HTTP_ACCEPT} "text\/vnd\.wap\.wml|application\/vnd\.wap\.xhtml\+xml" [NC,OR]
 
# Mobile user agent tests
RewriteCond %{HTTP_USER_AGENT} "sony|symbian|nokia|samsung|mobile|windows ce|epoc|opera" [NC,OR]
RewriteCond %{HTTP_USER_AGENT} "mini|nitro|j2me|midp-|cldc-|netfront|mot|up\.browser|up\.link|audiovox"[NC,OR]
RewriteCond %{HTTP_USER_AGENT} "blackberry|ericsson,|panasonic|philips|sanyo|sharp|sie-"[NC,OR]
RewriteCond %{HTTP_USER_AGENT} "portalmmm|blazer|avantgo|danger|palm|series60|palmsource|pocketpc"[NC,OR]
RewriteCond %{HTTP_USER_AGENT} "smartphone|rover|ipaq|au-mic,|alcatel|ericy|vodafone\/|wap1\.|wap2\.|iPhone|android"[NC,OR]
 
# Not Mac/iPad
RewriteCond %{HTTP_USER_AGENT} !macintosh [NC,OR]
RewriteCond %{HTTP_USER_AGENT} !ipad [NC]
 
# Redirect to mobile site
RewriteRule .* http://m.example.com/ [r=302]

# Has requested full site
RewriteCond %{HTTP_COOKIE} "nomobile=1" [NC,OR]
RewriteCond %{QUERY_STRING} nomobile [NC]
 
# Set ‘nomobile’ cookie
RewriteRule .* - [co=nomobile:1:.example.com:7200:/]

22Mar/11Off

‘Share’ links generator

I don't know about you, but I spend far too much of my time creating those blasted 'Share' links. Well, I did, until I wrote this pair of handy functions.

They will create share links for Facebook, twitter, delicious and StumbleUpon on the fly. Additionally you can pass any URL to create a share link to, and a title if you like.

You can create more definitions very easily by adding to the $types array. Use the terms #URL# and #TITLE# for the URL and title, or #UURL# and #UTITLE# for the urlencode() versions.

function share($type=null,$url=null,$title=null){
	// I store various page data in an $args array.
	// For this function, I'm be using the page title.
	// If you don't have a page title, it'll be ignored.
	global $args;
 
	// Type definitions
	$types=array(
		'fb'=>'http://www.facebook.com/sharer.php?u=#UURL#&t=#UTITLE#',
		'tw'=>'http://twitter.com/home?status=#UTITLE# #UURL#',
		'dl'=>'http://www.delicious.com/save?url=#UURL#&title=#UTITLE#',
		'sl'=>'http://www.stumbleupon.com/submit?url=#UURL#&title=#UTITLE#'
	);
 
	// Sanity checks
	if(!$type)return 'NO TYPE REQUESTED';
	if(!array_key_exists($type,$types))return 'UNDEFINED TYPE REQUESTED';

	// Build the share link
	if(!$url)$url=thisurl();
	$title=(!$title&&$args['page']['title'])?$args['page']['title']:$title;
	$find=array('#URL#','#TITLE#','#UURL#','#UTITLE#');
	$replace=array($url,$title,urlencode($url),urlencode($title));
	return str_replace($find,$replace,$types[$type]);
}
function thisurl(){
	// Return the full page URL
	$host=strtolower(array_shift(explode('/',$_SERVER['SERVER_PROTOCOL']))).'://';
	$uri=$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
	return($host.$uri);
}

// Usage:
echo share('fb');
echo share('tw');
echo share('dl');
echo share('su');
7Mar/11Off

PHP and Roman Numerals

After hunting around for functions that convert Arabic numbers to Roman numerals and vice versa, everything I found was huge (~100 lines) except for one very graceful function to convert Arabic numbers to Roman numerals. So I combined it with my own Roman to Arabic converter.

Pass it an int, a numeric string or a string of Roman numerals, and it will convert from one to the other.

Feel free to use the function in your own projects, but please keep the attributions intact.

function roman($num){
	$result='';
	$numerals=array(
		'M' =>1000,
		'CM'=>900,
		'D' =>500,
		'CD'=>400,
		'C' =>100,
		'XC'=>90,
		'L' =>50,
		'XL'=>40,
		'X' =>10,
		'IX'=>9,
		'V' =>5,
		'IV'=>4,
		'I' =>1
	);
	if(preg_match('/^[0-9]+$/',$num)){
		// To Roman: www.go4expert.com/forums/showthread.php?t=4948
		$n=intval($num);
		foreach($numerals as $roman=>$value){
			$matches=intval($n/$value);
			$result.=str_repeat($roman,$matches);
			$n=$n%$value;
		}
	}else{
		// To Arabic: www.beachcoder.co.uk/php-and-roman-numerals/
		$num=strtoupper($num);
		for($i=0;$i<strlen($num);$i++){
			$letter=$num[$i];
			$next=$num[$i+1];
			if($next&&$numerals[$next]>$numerals[$letter]){
				$result+=$numerals[$next]-$numerals[$letter];
				$i++;continue;
			}
			$result+=$numerals[$letter];
		}
	}
	return $result;
}

// Usage
echo roman('1000'); // M
echo roman(1100);   // MC
echo roman('MC');   // 1100
Filed under: PHP No Comments