So this entry isn’t about Perl but it is about a distant cousin, PHP, and the WordPress blogging software (v2.1+) spell checking bug that results in the notorious message:

Could not execute AJAX call, server didn’t return valid a XML

First let me start by saying that WordPress is a tremendous piece of software. I only have good things to say about it. Now that that is out of the way…

In WordPress v2.1+ there is a WYSIWYG option for writing posts, pages and comments. It isn’t full featured, but it is easy to use and provides the basics. It even includes a spell checker (which I am using as I write this). It is this spell checker that is causing so many people problems. When many people try to use the spell checker they receive the error message cited above. WordPress.org has a thread on this: http://wordpress.org/support/topic/101689

Having run into the same problem I decided to spend a couple of hours tracking down the problem and I can say that I have fixed it (on my system at least). My platform is running Windows Server 2003 with IIS and WordPress v2.2. I have patched my system and spell checker is working like a champ.

The Problem:
The nature of the problem has to do with a several things all coming together at once. The way that the spell checker is supposed to work is that it first checks for PHP’s built in spelling support called PSpell. In my case it looks like it has but when I try to use it I get Access Violation error (not a good sign). Failing that the spell checker will try to use the shell based PSpell library (in my case I had installed “ASpell.exe” on the server). I ensured that permissions were properly placed and that the IIS account could read/write the aspell files as well as temporary files. Failing all that the spell checker will then try to use Google’s online spell checker.

All of this spell checking is done using AJAX. If you are not familiar with AJAX, think of it as a way for a web page’s javascript code to call into a web server. The javascript spell checker calls into a URL on the server which calls the spelling library to look for misspelled words and make suggestions. The server returns this information and the javascript updates the web page by marking all misspelled words. To the user it looks like the web page did all the work — he doesn’t know that something called into the web server under the covers.

If something goes wrong with the AJAX all into the server you will get the error dialog. In my case there were several things that were going wrong:

  • Only Google was being used.
    My installation of PHP apparently has a problem with SSL. Somewhere in t bowels of the WordPress spell checker code that runs on the server it makes an SSL (HTTPS) call to Google’s spell checker web service. My PHP doesn’t recognize the SSL socket transport. Or at least that is what one error tells me. And since WordPress’ spell checker config.php file was set to only use Google, it would always fail.
  • PHP’s PSpell library wasn’t working.
    Even though my PHP has built in support for PSpell enabling that option in the config.php file only led to an Access Violation error.  Yes, I did copy the aspell-15.dll file into the path so that it could be located. And yes, I did enable the “extension=php_pspell.dll” line in the php.ini file.
  • The PSpell shell execution command was failing.
    Even when I enabled the PSpellshell class (to shell out and run aspell.exe) spell checking failed. It ends up that the redirection of STDERR into STDIN for the shelled execution of aspell.exe was causing the problem. Hint: this is what I fixed!
    The fix was to no longer shell out expecting the shell to redirect STDERR to STDIN. This often leads to problems on Windows machines. Instead use PHP’s proc_open() command to create the child process (aspell.exe) but use pipes for the process’ STDIN, STDOUT and STDERR. This way the code can read and write to the pipes and not worry about the pesky Windows shell.

The Solution:
To make a long story short I fixed the problem with the following. As always make backup files before you edit them in case all Hell breaks loose. You can download my modified versions of the 2 files described below: Updated TinyPspellShell.class.php and Updated config.php file. Note that you will need to rename these files to remove the trailing “.txt”.
Also note that these files came from WordPress v2.2 so they may need changes to fit into a v2.1 system.

  • Edit wp-includes/js/tinymce/plugins/spellchecker/config.php
    1. Uncomment the line that loads TinyPspellShell.class.php
    2. Comment out the lines that load TinyPspell.class.php and TinyGoogle.class.php
    3. Change the value assigned to $spellCheckerConfig['tinypspellshell.aspell']. Make it point to the actual aspell.exe such as:
      $spellCheckerConfig['tinypspellshell.aspell'] = '"c:\program files\ASpell\bin\aspell.exe"';
    4. You can ignore the temporary file location since my fix eliminates the temporary files.
  • Edit wp-includes/js/tinymce/plugins/spellchecker/classes/TinyPspellShell.class.php
    1. Perform a search on the file and locate the first line which assigns a value to $this->cmd. Change it to be:
         $this->cmd = $config['tinypspellshell.aspell'] . " -a --lang=". $this->lang." --encoding=utf-8 -H ";
      Note that you do not have to touch the second line which assigns something to $this->cmd. This is for non Windows implementations (such as Linux). If you want you can try changing that line to:
          $this->cmd = $config['tinypspellshell.aspell'] . " -a --encoding=utf-8 -H --lang=". $this->lang;
      Although have not tried this on a non Windows machine so I can not comment on how well it works.
    2. Comment out the first block of code of the checkWords() function. Essentially you will be commenting out everything from the beginning of this function through the line:
      @unlink($this->tmpfile);After the commented block of code place add the following line:
         $data = Win32Shell_Exec( $this->cmd, $wordArray );
    3. Comment out the following code from the getSuggestion() function:
         if ($fh = fopen($this->tmpfile, "w")) {
        fwrite($fh, "!\n");
        fwrite($fh, "^$word\n");
        fclose($fh);
        } else
        die("Error opening tmp file.");
        $data = shell_exec($this->cmd);

      Now place the following line after the commented block of code:
      $data = Win32Shell_Exec( $this->cmd, $word );
    4. At the end of the file add the following function:

function Win32Shell_Exec( $Command, $WordObject )
{
  $descriptorspec = array(
  0 => array( "pipe", "r"),
  1 => array("pipe", "w"), // stdout is a pipe that the child will write to
  2 => array("file", $TempFile . ".error-output", "w") // stderr is a file to write to
);
  $process = proc_open( $Command, $descriptorspec, $pipes);
  if( is_resource( $process ) )
  {
    $ProcessInputData = "";
    // $pipes now looks like this:
    // 0 => writeable handle connected to child stdin
    // 1 => readable handle connected to child stdout
    // Any error output will be appended to /tmp/error-output.txt
    if( is_array( $WordObject ) )
    {
      $ProcessInputData = "!\n";
      foreach( $WordObject as $Key => $Value)
      {
        $ProcessInputData .= "^" . $Value . "\n";
      }
    }
    else
    {
      $ProcessInputData = "!\n^$WordObject\n";
    }
    fwrite( $pipes[0], $ProcessInputData );
    fclose( $pipes[0] );
    while( !feof( $pipes[1] ) )
    {
      $myData .= fgets($pipes[1], 1024);
    }
    // It is important that you close any pipes before calling
    fclose($pipes[1]); // proc_close in order to avoid a deadlock
    $return_value = proc_close($process);
  }
  else
  {
    $myData = "COULD NOT CREATE PROCESS";
  }
  return( $myData );
}

Don’t forget to properly install ASpell.exe and ensure that the IIS user account (typically something called IUSR_xxxx where xxx is the machine name) is granted read permission on the aspell directory.

 _EDIT_2007.09.30

This has been tested on both WordPress v2.2 and v2.3.