Home > PHP/MySQL, WordPress > Posting to WordPress with PHP and XML-RPC

Posting to WordPress with PHP and XML-RPC

by Sam on August 8, 2009 · 30 comments

in PHP/MySQL, WordPress

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 = '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"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>WordPress Poster</title>
</head>
<body>
<?php
if(isset($_POST['submit'])) echo "Posted! <a href=\"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.

Short URL for this post:

http://bit.ly/14NIsu

Comments Map

Location data courtesy of GeoSmart

{ 4 trackbacks }

France Twitted by IMJA_LTD from Nord-Pas-de-Calais, France
August 10, 2009 at 7:36 am
United States Posting to WordPress with PHP and XML-RPC : XML Developer from New Jersey, United States
August 14, 2009 at 6:11 am
United States Posting to WordPress with PHP and XML-RPC (from Samuel Folkes Presents: Fuzzy Logic) from California, United States
August 22, 2009 at 5:36 pm
United States Remote Post Wordpress Autoblog « Kwamroo’s Blog from Texas, United States
January 6, 2010 at 2:44 am

{ 26 comments… read them below or add one }

1 Jamaica owen from Saint Andrew, Jamaica August 10, 2009 at 12:36 pm

xml rpc is a very useful technique to know, especially when dealing with external sites. Good to know if the user wants to post one place and update many sites.

2 Egypt ????? ????? from Ad Daqahliyah, Egypt August 17, 2009 at 4:28 am

Thank you :)

3 Algeria khalif62 from Algeria August 18, 2009 at 10:24 am

thannnnnnnnnnnnnnk you

4 United States Jimbo from Florida, United States September 10, 2009 at 10:18 am

transport error – could not open socket: 0 php_network_getaddresses: getaddrinfo failed: Name or service not known Posted!

5 Netherlands Antilles genji from Netherlands Antilles September 12, 2009 at 1:39 pm

just like i thought, doesn’t work.

Why can’t they create something that actually works, ugh

6 Jamaica Sam from Saint Andrew, Jamaica September 13, 2009 at 3:45 am

Twitter: @SamuelFolkes

@Jimbo – You’ll need to give me a little more information in order for me to help you fix that error…

@genji – Who are you referring to as ‘they’? Don’t be a douchebag. If you need help ask for it politely.

7 India Geekz from Orissa, India September 13, 2009 at 8:42 am

Can u help me to find out how to create pages, using this code?

8 Romania Daniel from Bucuresti, Romania September 13, 2009 at 12:00 pm

Nice post, thanks for sharing. It worked nice for me, except for posting the categories. As it turns out it always posts under Uncategorized.
I changed the following:
$content['categories'] = $_POST['category'];
Into this:
$content['categories'] = array($_POST['category']);
And it worked great.

Regarding Jimbo’s post earlier, I’ve seen that error when apache starts before connecting to the internet. So simply restarting apache may solve your problem.

9 Jamaica Sam from Saint James, Jamaica September 13, 2009 at 1:50 pm

Twitter: @SamuelFolkes

@Geekz – You’ll just have to change one line of code. In the postContent() function, change:

if(!$this->client->query('metaWeblog.newPost','',$this->uname,$this->pass,$content,true)) throw new Exception($this->client->getErrorMessage());

to:

if(!$this->client->query('wp.newPage','',$this->uname,$this->pass,$content,true)) throw new Exception($this->client->getErrorMessage());

This method to create pages utilizes the WordPress API rather than the MetaWeblog API.

@Daniel – Thanks. The code was written to by default create posts under Uncategorized. If you add other categories to the drop down select box it should work fine.

10 United States Robert Jordan from Tennessee, United States September 15, 2009 at 11:57 am

Is there any chance you will be able to make this into a package with a bit more tutorial for those of us with less confidence in cut and paste from your code snippets? Maybe that’s a silly request but I guess I’m a silly guy. Plus, do I need to obtain the MetaWeblog API or is it already present in WordPress?

11 Jamaica Sam from Saint Andrew, Jamaica September 15, 2009 at 3:42 pm

Twitter: @SamuelFolkes

@Robert – Not a silly request at all, I certainly could. I’ll package the code into a zip file with instructions and add a link at the end of the tutorial. The MetaWeblog API is built in, you won’t need to obtain anything else.

12 United States Robert Jordan from New Jersey, United States September 15, 2009 at 8:45 pm

After first getting exposed to the XML-RPC and MetaWeblog API by your excellent presentation, I am this evening discovering about the many Weblog clients that utilize the same tools as your PHP implementation. Have you used any of these Weblog clients and do you recommend them, or was dissatisfaction with some element what drove you to create your own solution?

13 Jamaica Sam from Saint Andrew, Jamaica September 17, 2009 at 12:08 pm

Twitter: @SamuelFolkes

I have used a few of these clients, I love Windows Live Writer and I also use the WordPress BlackBerry app both of which utilize the XML-RPC protocol. This code I wrote was basically to allow users to post to a blog where most of the content was user generated without these users having to register and login. As Owen said above, with a little tweaking it can also come in handy when you need to update multiple blogs at once with the same content.

14 Indonesia duratia from Jakarta Raya, Indonesia September 23, 2009 at 1:34 pm

Hi, what if I don’t want to use image to post my blog? What section must I omit?

Thanks.

15 Indonesia wambit from Jawa Tengah, Indonesia September 24, 2009 at 1:02 pm

First of All.. I get this error..

transport error – could not open socket: 0 php_network_getaddresses: getaddrinfo failed: Name or service not known Posted!

But after I trace the code.. I found that this line that make error occurs.
$this->client = new IXR_Client($wpURL);

and then I change into this..
$this->client = new IXR_Client($this->wpURL);

Great!!

16 Ireland Gabriele Massari from Limerick, Ireland October 12, 2009 at 4:25 am

Hi

The class works great, and I’m using it for multiple entry ( with chekboxes blog ) but categories doesn’t works…without errors, just the class don’t send the categories to the blog…

17 Australia Ben from Victoria, Australia October 16, 2009 at 10:56 pm

Twitter: @BK4D

Hi, I’m implementing it a little differently. Instead of using a form I’m extracting some string data from xml (using domDocument) and the putting the relevant data into an associative array ($content['title'] etc) much like your example on form submit. I keep getting the error ‘parse error. not well formed’ though. There’s nothing really complex in there. Do you know what would be causing this?

18 Australia Ben from Victoria, Australia October 16, 2009 at 11:43 pm

Twitter: @BK4D

Hi again. I’ve now worked out where I’m getting the ‘not well formed’ error. I’m trying to pass the $content['dateCreated'] value so that I can back-date the post to a specified date. It all works fine when I remove that. I can’t find anywhere what format this value should take. It’s currently a string like ‘20080121T12:38:30′ but I’ve tried various options, none of which work. Will have to keep hunting

19 Jamaica Sam from Saint Andrew, Jamaica October 17, 2009 at 12:02 am

Twitter: @SamuelFolkes

Ben, your date needs to be formatted in ISO-8601 format, i.e YYYY-MM-DD to be accepted. For more info about about the ISO-8601 date format see here.

20 Jamaica Sam from Saint Andrew, Jamaica October 17, 2009 at 12:07 am

Twitter: @SamuelFolkes

@duratia – If you don’t want an image to be posted along with your text then just ignore the image upload field in the form.

@Gabriele – The category has to exist on the blog, otherwise you’ll get errors.

21 Australia Ben from Victoria, Australia October 24, 2009 at 8:23 am

Twitter: @BK4D

Yeah thanks Sam. I just set the dateCreated value as new IXR_Date(“My date string”) and it works fine.

22 Philippines Bryan from Rizal, Philippines November 4, 2009 at 5:52 am

Twitter: @sphex1987

Hello Sam,

Can you give me an example that uses metaWeblog.getCategories in PHP and will display the list of categories? Thanks!

23 United States Dustin Dempsey from Idaho, United States November 4, 2009 at 5:28 pm

Hey, I was looking to allow users in my WordPress install to provide me with their WordPress information through their profile and have a script auto-magically post my new posts on my blog to theirs.

I’d imagine this can be achieved with this script, but I’m no programmer so I don’t understand how to do it.

Would you be able to implement something like that? It would be a paid job.

Thanks.
Dustin

24 United States Patrick Newcomb from Michigan, United States November 15, 2009 at 5:15 am

Twitter: @TrailReport

Looks like great code…. however, (like Robert, above) I am also not to good with php… and am not sure where all the snippets go. Any chance of a tutorial mentioned above. I just need to create an html form page that would remotely post to a wordpress blog (not hosted at wordpress.com)… Thanks of any help….

25 Philippines gio from Benguet, Philippines November 27, 2009 at 10:29 am

is there any 100% sure way to know that a post is successfully posted?

i’ve just tried your script and sometimes i get this error:
‘transport error – HTTP status code was not 200′ – although the posting is successful.

So what does this error really mean?

26 Ireland Hasnat from Dublin, Ireland January 3, 2010 at 7:54 am

thats awesome, but update link of class on 3rd paragraph its gone 404 at the moment

Leave a Comment

You can use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

Previous post:

Next post: