Posting to wordpress with php and xml-rpc

Recently, i’ve been working on a few projects that have required me to extend my programming reach in order to acheive things not normally encountered in your typical project. One of the things i’ve been required to accomplish with PHP lately is to post to WordPress automatically via an HTML form. On exploring the possible options I came across the XML-RPC and AtomPub remote publishing protocols. After briefly reviewing both protocols, for the sake of simplicity, I chose to use XML-RPC.

To quote the XML-RPC homepage:

It’s (XML-RPC) a specification and set of implementations that allow software running on disparate operating systems, running in different environments to make procedure calls over the internet. It’s remote procedure calling using HTTP as the transport and XML as the encoding.

So, in essence, XML-RPC here will allow us to use our software system to execute procedures on another software system (WordPress) over the internet. There is an experimental extension called phpxmlrpc that handles the formatting of XML-RPC requests but it does not usually come preinstalled with PHP. So, for the purposes of this article, we will be using the Incutio XML-RPC Library for PHP. You can find this class in your wp-includes folder, its called class-IXR.php or you can download the class here. Please note that for this to work, you will need to enable the XML-RPC remote publishing protocol under settings in the admin panel of your WordPress blog.

There are a few protocols supported by XML-RPC, among them are the MetaWeblog API, the Blogger API and the WordPress API. The WordPress API is by far the most flexible, containing methods that perform a wide variety of tasks including handling comments on your blog. As far as I know however, to date the WordPress API does not have any method which will allow us create a new post. Therefore, we will be using the MetaWeblog API which has fewer methods, but will allow us to do all we need to do for the purposes of this article. The MetaWeblog API provides us with the following methods:

  • metaWeblog.newPost
  • metaWeblog.editPost
  • metaWeblog.getPost
  • metaWeblog.getRecentPosts
  • metaWeblog.getCategories
  • metaWeblog.newMediaObject
  • metaWeblog.deletePost
  • metaWeblog.getTemplate
  • metaWeblog.setTemplate
  • metaWeblog.getUsersBlogs

So, now that we have established what XML-RPC is and some of the ways to work with it, lets delve into some code. We will begin by defining a class that will handle interactions with the Incutio Library and we will name it remotepost.class.php:

<?php
 
class remotePost
{
private $client;
private $wpURL = '/web/20100313172315/http://www.mywpblog.com/xmlrpc.php ';
private $ixrPath = '/path/to/class-IXR.php';
private $uname = 'myusername';
private $pass = 'mypass';
private $maxSize = 2097152; //2MB
private $tempDir = '/path/to/temporary/image/directory';
public $postID;

What we’ve done here is we’ve defined a class called remotePost and declared eight variables, seven private and one public. Here is a listing of the variables and their contents:

  • private $client – Will house our IXR_Client object.
  • private $wpURL – The url to the xmlrpc.php file of the blog we are posting to.
  • private $ixrPath – The path to the Incutio Library class.
  • private $uname – The username we will use to be authenticated with our WordPress blog.
  • private $pass – The password associated with the username we are using.
  • private $maxSize – The maximum allowed size of image attachments.
  • private $temDir – The temporary directory in which we will store our uploaded images.
  • public $postID – Will contain the post ID of our newly created post.

Now, lets turn our focus to the class methods. First we will look at the class constructor:

function __construct($content)
{
if(!is_array($content)) throw new Exception('Invalid Argument');
include $this->ixrPath;
$this->client = new IXR_Client($this->wpURL);
$imgURL = $this->uploadImage();
$this->postID = $this->postContent($imgURL,$content);
}

Our class constructor takes one argument, $content, which is an array containing the post title, category and content. It checks to ensure that the argument received is an array and if it isn’t, an exception is thrown. It then, includes the Incutio XML-RPC Library, instantiates an IXR_Client object, calls the uploadImage() method and stores the response in the variable $imgURL. It then calls the postContent() method passing in the $imgURL and $content variables as arguments and stores the return value in the $postID class variable. Lets now have a look at our uploadImage() and postContent() methods:

private function uploadImage()
{
$fileName = $_FILES['pic']['name'];
$fileType = $_FILES['pic']['type'];
$fileTempName = $_FILES['pic']['tmp_name'];
$fileSize = $_FILES['pic']['size'];
$fileError = $_FILES['pic']['error'];
if($fileError == UPLOAD_ERR_NO_FILE)
{
$imgURL = null;
return $imgURL;
}
else
{
if($fileError == UPLOAD_ERR_OK)
{
if($fileSize > $this->maxSize) throw new Exception('File too large!');
if(!eregi('image/',$fileType)) throw new Exception('Uploaded file is not an image!');
$fileInfo = getimagesize($fileTempName);
if(!eregi('image/',$fileInfo['mime'])) throw new Exception('Uploaded file is not an image!');
$fileName = rand(0,time()) . $fileName;
if(!move_uploaded_file($fileTempName,"$this->tempDir/$fileName")) throw new Exception('Unable to move uploaded image!');
$filePath = "$this->tempDir/$fileName";
}
}
 
$img = file_get_contents($filePath);
$encoded = new IXR_Base64($img);
$fileName = basename($filePath);
$imgData['name'] = $fileName;
$imgData['type'] = $fileInfo['mime'];
$imgData['bits'] = $encoded;
$imgData['overwrite'] = false;
if(!$this->client->query('metaWeblog.newMediaObject','',$this->uname,$this->pass,$imgData)) throw new Exception($this->client->getErrorMessage());
unlink($filePath);
$info = $this->client->getResponse();
$imgURL = $info['url'];
return $imgURL;
}

The uploadImage() method is does a bit more than the constructor. It separates the contents of the $_FILES array into individual variables and then checks whether or not a file has been uploaded. If no file has been uploaded it simply returns null. If a file has been uploaded and the upload was successful, It performs a series of checks to ensure that the uploaded file is indeed an image and then moves the file to the temporary directory specified in our $tempDir variable. It then reads the contents of the image we uploaded into the $img variable which is passed to an instance IXR_Base64 class. The IXR_Base64 class encodes our image data in base64 in order to make the data better able to withstand transport over the internet. An array called $imgData is created which contains the file name of the image, the mime type, the base64 encoded image ‘bits’ and a boolean value to indicate whether or not the script will overwrite an existing file with the same name. The query() method of the $client object is then called with the following arguments:

  • metaWeblog.newMediaObject – This is the MetaWeblog API method that uploads media files.
  • ” – This is the BlogID argument that can safely be left blank (WordPress 2.7 or later).
  • $this->uname – This is the username of a user on your blog which has posting privileges.
  • $this->pass – This is the password associated with the above username.
  • $imgData – This is an array containing the image data (name, type, bits, overwrite)

If this query fails, an exception is thrown and passed the response of the getErrorMessage() method of the $client object. An array of response data is created via the getResponse() method of the $client object and the URL of the uploaded file is extracted and returned.

private function postContent($imgURL,$content)
{
if(isset($imgURL))
{
$imgString = "<img src="$imgURL" class="alignleft" title="$content[title]" alt="$content[title]" width="300" />";
$content['description'] = $imgString . $content['description'];
}
if(!$this->client->query('metaWeblog.newPost','',$this->uname,$this->pass,$content,true)) throw new Exception($this->client->getErrorMessage());
return $this->client->getResponse();
}
}
?>

The postContent() method takes two arguments, $imgURL which is the URL of the image uploaded to WordPress by the uploadImage() method and $content which is an array containing the data of our post. It amends the post description to include an HTML reference to the uploaded image (if an image is uploaded) and then it calls the query() method of the $client object with the following arguments:

  • metaWeblog.newPost – This is the MetaWeblog API method that creates a new post.
  • ” – This is the BlogID argument that can safely be left blank (WordPress 2.7 or later).
  • $this->uname – This is the username of a user on your blog which has posting privileges.
  • $this->pass – This is the password associated with the above username.
  • $content – This is an array containing the post data (title, category, content).
  • true – This is a boolean value which determines whether or not the post should be published immediately. If set to true, the post will be published immediately, if set to false, the post will be saved as a draft.

If this query fails, an exception is thrown and passed the response of the getErrorMessage() method of the $client object. The post ID of the newly created post is then returned to the calling function via the getResponse() method of the $client object.

Now, we’ll create a simple HTML form to accept our input:

<?php
if(isset($_POST['submit']))
{
include "remotepost.class.php";
$content['title'] = $_POST['title'];
$content['categories'] = $_POST['category'];
$content['description'] = $_POST['description'];
try
{
$posted = new remotePost($content);
$pid = $posted->postID;
}
catch(Exception $e)
{
echo $e->getMessage();
}
}
 
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"/web/20100313172315/http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="/web/20100313172315/http://www.w3.org/1999/xhtml">
<head>
<title>WordPress Poster</title>
</head>
<body>
<?php
if(isset($_POST['submit'])) echo "Posted! <a href="/web/20100313172315/http://myblog.com/?p=$pid">View Post</a><br/><br/>";
 
?>
<form enctype="multipart/form-data" method="post" action="#">
<input type="hidden" name="MAX_FILE_SIZE" value="2097152" />
<input type="file" name="pic" />
Title
 
<input type="text" name="title" />
Category
 
<select name="category">
<option value="Uncategorized">Uncategorized</option>
</select>
Description
 
<input type="text" name="description" />
<br/>
<input type="submit" value="Submit" name="submit" />
</form>
</body>
</html>

What happens here is relatively simple. When the submit button is clicked, the remotepost.class.php file is included and an array is created with all our post details and passed to an instance of the remotePost class which, in tandem with the Incutio XML-RPC Library handles all of the heavy lifting. We then extract the post ID from the object and create a link to the post we have created.

This system is a very simple and effective way to remotely post to WordPress blogs. It can be extended significantly to be more robust and to support more features such as the ability to upload attachments other than images and to have the category select box automatically populated with all the categories that exist on the WordPress blog you are posting to. It is my sincere hope that people will find this article useful. You are of course welcome to leave a comment with your thoughts and feedback on this article.






Comments are Closed