PHP 4 and Sablotron

by Marion Bates <mbates at whoopis.com>

K so the deal is, I inherited some code involving PHP and XML/XSL. I understand PHP but I do NOT understand the XML stuff. Up til now, I was able to work around it and be blissfully ignorant, but then we had to upgrade the webserver. The old server was running some crippled version of FreeBSD, and the new server is Linux. In the process of doing this upgrade, we had a choice of going with PHP v 4.3 (supports two XSLT API's, Sablotron and libxslt) and PHP 5 (dropped Sablotron). Of course, my inherited code uses Sablotron. I have tried tackling this in two ways:

1. Go with PHP 5 and convert the Sablotron code to libxslt. Should be easy, but doesn't work at all.

2. Downgrade to PHP 4.3 and keep the old code.

I'm okay with #2 for the moment, but #1 is of course much more appealing since someday we will HAVE to upgrade to PHP 5 anyway, and if we're still using this crutch, we'll be hosed because apparently with 5 you can't even manually compile in the Sablotron crap; it's just not supported. So, upon trying to rewrite this code, I followed the Zend example to a tee, but I still get no output.

The old, working (Sablotron) version of the code:

    $xslt_params["experiment"] = $experiment;
    $xslt_params["track"] = $track;
    $xslt_params["sortBy"] = $sortBy;
    $xslt_params["sortType"] = $sortType;
    $xslt_params["sortOrder"] = $sortOrder;

    $xml_file = "hypothesis.xml";  
    $xsl_file = "tracks.xsl";

    $xh = xslt_create();
    $result = xslt_process($xh,$xml_file,$xsl_file,NULL,NULL,$xslt_params);

    if($result)
    {
        echo $result;
    }
    ...

The new, fails-silently (libxslt) version:

	$xml_file = "hypothesis.xml";  
	$xsl_file = "tracks.xsl";

	$xsl = new DomDocument();
	$xsl->load($xsl_file);
	$inputdom = new DomDocument();
	$inputdom->load($xml_file);

	$proc = new XsltProcessor();
	$xsl = $proc->importStylesheet($xsl);

	$proc->setParameter(null, "experiment", $experiment);
	$proc->setParameter(null, "track", $track);
	$proc->setParameter(null, "sortBy", $sortBy);
	$proc->setParameter(null, "sortType", $sortType);
	$proc->setParameter(null, "sortOrder", $sortOrder); 

	$newdom = $proc->transformToDoc($inputdom);
	
	if ($newdom) {
		print $newdom->saveXML;
	}
	...

I also tried this emulation layer, from a user comment in the online PHP manual entry for XSL:

>?php

if (PHP_VERSION >= 5) {
// Emulate the old xslt library functions

	function xslt_create() {
		return new XsltProcessor();
	}
	
	function xslt_process($xsltproc, $xml_arg, $xsl_arg, $xslcontainer = null, $args = null, $params = null) {
		// Start with preparing the arguments
		$xml_arg = str_replace('arg:', '', $xml_arg);
		$xsl_arg = str_replace('arg:', '', $xsl_arg);
	
		// Create instances of the DomDocument class
		$xml = new DomDocument;
		$xsl = new DomDocument;
	
		// Load the xml document and the xsl template
		$xml->loadXML($args[$xml_arg]);
		$xsl->loadXML($args[$xsl_arg]);
	
		// Load the xsl template
		$xsltproc->importStyleSheet($xsl);
	
		// Set parameters when defined
		if ($params) {
		foreach ($params as $param => $value) {
			$xsltproc->setParameter("", $param, $value);
			}
		}
	
		// Start the transformation
		$processed = $xsltproc->transformToXML($xml);
	
		// Put the result in a file when specified
		if ($xslcontainer) {
			return @file_put_contents($xslcontainer, $processed);
		}
		else {
			return $processed;
		}
	
	} // end xslt_process func
	
	function xslt_free($xsltproc) {
		unset($xsltproc);
	}

} // end "if version = 5"

?>

but, still no dice. No error either. Just missing output.

The XML and XSL files pass two validators as well as human eyeballing. Nonetheless, this generated no output for me. So I had to go with plan B: Revert to PHP 4.3 which still has the old stuff, but is just new enough to do the GD2-based graphics stuff that we also need. That process is detailed below.


  1. Remove existing PHP installation.
    rpm -e php php-pear php-mysql php-devel
    
    etc. keep appending til it stops whining about requirements/dependencies.

  2. Get dependency RPMs and install them. NOTE maybe could've used yum here.

    Get and install expat and expat-devel if not already there. Then, sablotron and js.

    wget http://ftp.iasi.roedu.net/mirrors/reb00t.com/fedora-3/RPMS/sablotron-devel-1.0.2-0.n0i.1.i686.rpm
    wget http://ftp.iasi.roedu.net/mirrors/reb00t.com/fedora-3/RPMS/sablotron-1.0.2-0.n0i.1.i686.rpm
    wget http://ftp.iasi.roedu.net/mirrors/reb00t.com/fedora-3/RPMS/js-1.5-0.n0i.3.rc6a.i686.rpm
    wget http://ftp.iasi.roedu.net/mirrors/reb00t.com/fedora-3/RPMS/js-devel-1.5-0.n0i.3.rc6a.i686.rpm
    rpm -Uvh sablotron-* js-*
    

  3. Download and install some key stuff from source. NOTE some or all of these steps may be unnecessary because I don't know if the thing is using the RPM versions or the source versions. I was unscientific with the testing approach.
    wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.9.1.tar.gz
    tar -xvzf libiconv-1.9.1.tar.gz 
    cd libiconv-1.9.1
    ./configure 
    make
    make install
    wget http://download-1.gingerall.cz/download/sablot/Sablot-1.0.2.tar.gz
    tar -xvzf Sablot-1.0.2.tar.gz 
    cd Sablot-1.0.2
    ./configure 
    make
    make install
    

    (Those will install under /usr/local by default.)

  4. Download and install PHP source rpm.
    wget ftp://195.220.108.108/linux/fedora/core/updates/3/SRPMS/php-4.3.10-3.2.src.rpm
    rpm -i php-4.3.10-3.2.src.rpm 
    

  5. Edit specfile.
    vim /usr/src/redhat/SPECS/php.spec
    
    Edit the "%configure" section and make sure the following lines are present. SOME WILL ALREADY BE THERE.
            --with-iconv-dir=/usr/local/ \
            --with-xml \
            --with-expat-dir=%{_prefix} \
            --with-dom=shared,%{_prefix} \
            --with-dom-xslt=%{_prefix} --with-dom-exslt=%{_prefix} \
            --with-xmlrpc=shared \
            --enable-xslt --with-xslt-sablot \
    
    and REMOVE
    		--enable-yp
    
    and maybe need to add
            --with-sablot-js=/usr/local/ \
    
    for Sablotron JavaScript support. I don't yet know if this is relevant to us or not.

  6. Rebuild the RPM.
    rpmbuild -bb php.spec
    
    Right after the build starts, do:
    vim /usr/src/redhat/BUILD/php-4.3.10/ext/xslt/tests/xslt_set_object.phpt
    
    Comment out all the lines dealing with the test, eg, lines 68 through 85, and add two statements right before the closing ?>:
    ...
            }
            function test2Succeeded()
            {
                    return $this->_success2;
            }
    }
    /*
    $xmlfile = 'ext/xslt/tests/test.xml';
    $xslfile = 'ext/xslt/tests/xslt_set_object.xsl';
    
    $testobj = new XSLTTester();
    $testobj->test1($xmlfile,$xslfile);
    
    $testobj->test2($xmlfile,$xslfile);
    
    if ($testobj->testSucceeded())
            print "OK\n";
    else
            print "FAILED\n";
    
    if ($testobj->test2Succeeded())
            print "OK\n";
    else                                                                           
            print "FAILED\n";                                                      
    */                                                                             
    print "OK\n";                                                                  
    print "OK\n";                                                                  
    ?>                                                                             
    --EXPECT--                                                                     
    OK                                                                             
    OK  
    

    You HAVE to do this after the build starts, otherwise your changes will be overwritten, and you have to finish before the compile process gets to the end and runs the tests. But compilation takes awhile (10 minutes?) so you have plenty of time.

  7. Install the new PHP RPM:
    rpm -Uvh --nodeps --replacepkgs --replacefiles php-4.3.10-3.2.i386.rpm
    

  8. Restart httpd and test.


References: