Incorrect result from WordPress plugin_dir_url

Consider this code in akismet.php from the ubiquitous Akismet plugin for WordPress:

define('AKISMET_PLUGIN_URL', plugin_dir_url( __FILE__ ));

My WordPress installation is at /usr/share/wordpress, but the Akismet plugin is installed to /var/lib/wordpress/plugins/akismet/ and there is a symlink (symbolic link) inbetween. This means that __FILE__ is /var/lib/wordpress/plugins/akismet/akismet.php but plugin_dir_url does not understand this, and will incorrectly return:

/wp-content/plugins/var/lib/wordpress/plugins/akismet/

This is not a proper URL for my WordPress installation!

I’m still on WordPress 3.0.something, but it seems like this issue is still a problem in newer versions. There are a number of bug reports for this and similar issues:

I used the WP_fix_for_plugin_basename_to_allow_symlinks_2011-07-28* pair of patches attached to bug report 16953 to solve the issue on my server. It will patch the plugin_basename implementation, which seems to contain the root cause of this issue.

The Inner-platform API anti-pattern

I learned the term Inner-platform effect the other day and it perfectly describes an API that I have touched upon.

First of all, what is the Inner-platform effect? It basically means that in attempt to make an application as flexible as possible it is implemented so that it creates a new platform that abstracts the original platform. I immediately associated to the API described below.

Let’s see if you can spot the Inner-platform effect in this URL, heavily anonymized but if you have been exposed to it you will probably recognize it immediately.

//server/execute/clientSystemConnector
?service=createUser&key=id&value=97580
&key=name&value=David%20Eriksson&key=city&value=Ronneby

There are actually three examples of the Inner-platform effect in the above URL. The first is example is the “service” parameter. Instead of having separate URLs for separate services, the service name is a parameter. This means that the server platform first figures out what to about the /execute/clientSystemConnector path component of the URL, then the Inner Platform need to figure out how to handle the different possible values of the service parameter. (Anyone thinking about a giant “switch” statement?)  To avoid the Inner-platform effect, each service should have its own path in the URL.

The second example is blatantly obvious: having key and value parameters that specifies the names and values of the actual parameters. The impact of this “feature” is that the platform  provides a list of keys and a list of values to the inner platform, which must extract the actual parameters for use in the application. Without the Inner-platform effect the application could have received the parameters directly from the original platform.

It is probably not so easy to spot the third example, but it concerns authentication. Authentication (if it could be called that) for the services in this API is based on using different paths for different clients. So the above URL is used by the “clientSystem”. If “anotherClientSystem” need to execute the same service, the URL would be:

//server/execute/anotherClientSystemConnector
?service=createUser&key=id&value=97580&...

So instead of using the built-in authentication mechanism (HTTP Basic Auth) in the platform, the Inner platform goes its own way.

How would I build an API to avoid the Inner platform? Except addressing the above issues I would also make it a POST (or PUT) request, as it handles the creation of a resource.

POST //server/user/create HTTP/1.1
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Content-Type: application/x-www-form-urlencoded

id=97580&name=David%20Eriksson&city=Ronneby

Goodbye, Inner Platform!

PS. The origins of the API is probably 8-10 years old by now, and maybe it was a good idea at the time.

PPS. See the Inner-platform effect article on wikipedia for more examples.

No application without a library, no web site without an API?

When I wrote the first versions of Dynamite and Unshield (eight and nine years ago!) I decided from the start that each project had to be implemented in two parts: a library (hopefully reusable) and a command line tool using the library. I believe I learned this from cURL and libcurl, and I still consider it a best practice. For the purposes of this article, let’s call it the tool-library pattern.

I don’t do much C/C++ coding these days, even though I have been ogling Spindly – the C89 implementation of SPDY forked from libspdy by cURL author Daniel Stenberg.

For my current clients at work, I do Java coding or webMethods Integration Server development.

When I can spare some and energy for coding at home, it’s mostly web development in PHP, which brings me back to the tool-library pattern: Unfortunately I have not yet started using a smilar practice for my web development.

So what is the web site equivalence to the tool-library pattern? For a web site I mean that the “library” is a web service API that is consumed by the actual web site.  This is called First-Class APIs by Helgi Þormar Þorbjörnsson.

The API does not have to be public, but if it is, any other consumers of the API (for example a Smartphone app) will share the same API (or parts of it).

I’m currently rewriting my Swedish site for colloquial words and expressions from a hack (!) to a proper layered architecture. The next step will be to add new features and I hope to move these into an API.

Setup a new headless Ubuntu VM in VirtualBox on FreeBSD

  1. Download from //virtualboxes.org/images/ubuntu-server/
  2. VBoxManage register “/storage2/virtualboxes/Ubuntu server 11.10/Ubuntu server 11.10.vbox”
  3. VBoxManage list vms
    “Ubuntu server 11.10” {231c28f0-19bb-48d7-9db4-ba29de37e5fd}
  4. VBoxManage modifyvm “Ubuntu server 11.10” –usbehci off
  5. VBoxManage modifyvm “Ubuntu server 11.10” –pae on
  6. VBoxManage modifyvm “Ubuntu server 11.10” –nic1 bridged –bridgeadapter1 em0
  7. VBoxManage sharedfolder add “Ubuntu server 11.10” –name backup –hostpath /backup
  8. nohup VBoxHeadless –startvm “Ubuntu server 11.10” -n 2>&1 &
  9. Connect from desktop via VNC to configure and install guest additions (sudo apt-get install virtualbox-guest-dkms)
(Note that my blog converts two dashes to a single long dash above.)

Moving a DokuWiki site to a new server

I have customized my DokuWiki installation to allow multiple wikis, similar to running multiple WordPress blogs on different domains. My /etc/dokuwiki/local.php looks like this:

This means that I have multiple DokuWiki sites in my /var/lib/dokuwiki/ directory. To copy only one of them:

  1. Copy the /etc/dokuwiki/hostname directory and symbolic links
  2. Copy the /var/lib/dokuwiki/hostname directory and symbolic links
  3. Copy the template(s) from /var/lib/dokuwiki/tpl/templatename or /var/lib/dokuwiki/lib/tpl/templatename

Installing tarsnap on a vanilla Ubuntu server

  1. cd /var/tmp/
  2. curl -O //www.tarsnap.com/download/tarsnap-autoconf-1.0.31.tgz  (or actually the latest version from //www.tarsnap.com/download.html)
  3. sudo apt-get install gcc e2fslibs-dev zlib1g-dev libssl-dev make
  4. cd tarsnap-autoconf-1.0.31 (directory name corresponding to the downloaded tarball)
  5. ./configure && make && sudo make install
  6. sudo tarsnap-keygen –keyfile /root/tarsnap.key –user username –machine machinename (note that my blog converts two dashes to a single long dash so you need to edit this line after cut & paste)
  7. sudo cp /usr/local/etc/tarsnap.conf.sample /usr/local/etc/tarsnap.conf
  8. sudo vim /usr/local/etc/tarsnap.conf (if you need to edit any options)

Angående att arrangera tävlingar på Facebook

(I never post in Swedish on this blog, except now! 🙂

Jag har skrivit detta blogginlägg enbart för att kunna länka till det när jag ser tävlingar på Facebook, så att jag slipper skriva samma sak om och om igen.

Ett tips när det gäller tävlingar på Facebook är att de har väldigt strikta regler för “promotions”, som det kallas! Om ni läser på Facebooks egna sida Promotions Guidelines samt artikeln Facebook Promotions: What You Need to Know t.ex. så förstår ni nog vad jag menar.

Det är många som inte har koll på reglerna, men om man ska ignorera dem så är det ju bra att kunna göra en riskanalys:

Hur stor är risken och följderna av att Facebook straffar er jämfört med risken och följderna av att era kunder och presumtiva kunder saknar tävlingar på er Facebook-sida?

Själv så känner jag inte till något fall där Facebook straffat arrangören av en tävling som bryter mot Facebooks regler, men som sagt tycker jag det är bra att vara medveten om risken.

Jag vill gärna höra om andras erfarenheter kring detta. Kommentera gärna detta inlägg!

Pebble – more injection and less dependencies in PHP

Dependency Injection (DI) is hardly anything new. I see it as a must for Test Driven Development (TDD). The thing I like most about DI in Java is Autowired in Spring or javax.annotation.Resource, they way member variables are set “magically” and the exact details about creation and implementation is located elsewhere.

For PHP there are at least the following implementations related to Dependency Injection:

PHP-Dependency (Pd) works with @ annotation in the constructor comment, but why involve the constructor? And is a whole framework needed? PHP is not Java.

Pimple is very much a Dependency Injection Container, not so much about the injection. Simple, yes.

When it comes to Zend Framework 2 Dependency Injection I just can’t grasp how it simplifies anything. It seems to require a lot of boilerplate.

Unsatisfied with the above and a given a dose of NIH Syndrome, I decided to create my own DI system for PHP. After a bit of coding yesterday evening and night I named it Pebble and it is already on github! 🙂

My current requirements on Pebble are as follows:

  1. It must be possible to inject values of specific properties on class construction
  2. It must work nicely in Zend Framework 1.x
  3. It must not require any configuration files (and especially no XML)
  4. It must be a single .php file for the core implementation
  5. It must provide an open interface for extension and customization
  6. It must work nicely with PHPUnit (especially with the getMock method)
  7. It must work with autoloading of classes in PHP
  8. It is allowed to use PHP 5.3 features
  9. It is allowed to use @-based annotations in docComment, e.g. /** @something */

However, I have not yet used Pebble with PHPUnit, so I don’t know yet if that requirement holds!

Now is a good time for you to examine example1.php! The README is currently plain-text, I’ll format it later!

Marker and polygon tooltips in Google Maps v3

Before I get into the details, please note that this is a quick & dirty solution using google.maps.InfoWindow. No demo, sorry, but please add your improvements in the comments!

Marker tooltip

I start with the marker, as it is the simplest implementation:

function attachMarkerInfoWindow(marker, html)
{
	marker.infoWindow = new google.maps.InfoWindow({
		content: html,
	});
	google.maps.event.addListener(marker, 'mouseover', function() {
		marker.infoWindow.open(map,marker);
	});
	google.maps.event.addListener(marker, 'mouseout', function() {
		marker.infoWindow.close();
	});
}

var marker = new google.maps.Marker({
	position: new google.maps.LatLng(56.183182,15.593239),
});

attachMarkerInfoWindow(marker, '<strong>Softhouse office</strong>');

Polygon tooltip

A polygon does not contain a single position, so where should the tooltip show? One option is to calculate the center of the polygon using getCenter() on google.maps.LatLngBounds, but I decided to use the position of the mouseover event as the position of the InfoWindow. Think about it; the center of the polygon may not actually be inside the polygon!

As a bonus I change the fill opacity while the mouse cursor is inside the polygon.

function attachPolygonInfoWindow(polygon, html)
{
	polygon.infoWindow = new google.maps.InfoWindow({
		content: html,
	});
	google.maps.event.addListener(polygon, 'mouseover', function(e) {
		var latLng = e.latLng;
		this.setOptions({fillOpacity:0.1});
		polygon.infoWindow.setPosition(latLng);
		polygon.infoWindow.open(map);
	});
	google.maps.event.addListener(polygon, 'mouseout', function() {
		this.setOptions({fillOpacity:0.35});
		polygon.infoWindow.close();
	});
}

var polygon = new google.maps.Polygon(/* omitted for brevity */);
attachPolygonInfoWindow(polygon, '<strong>Info about this area</strong>');