Archive for the ‘PHP’ Category.

Renaming a DOMNode in PHP

A recent work assignment had me using PHP to pull HTML data into a DOMDocument instance and renaming some elements, such as b to strong or i to em. As it turns out, renaming elements using the DOM extension is rather tedious.

Version 3 of the DOM standard introduces a renameNode() method, but the PHP DOM extension doesn’t currently support it.

The $nodeName property of the DOMNode class is read-only, so it can’t be changed that way.

A node can be created with a different name in the same document, but if you specify a value to go along with it, any entities in that value are automatically encoded, so it’s not possible to pass in the intended inner content of a node if it contains other nodes.

The only method I’ve found that works is to replicate the attributes and child nodes of the original node. Attributes are fairly easy, but I ran into an issue replicating children where only the first child of any given node was replicated within its intended replacement and the remaining children were omitted. Here’s the original code that was exhibiting this behavior.

foreach ($oldNode->childNodes as $childNode) {
    $newNode->appendChild($childNode);
}

The reason for this behavior is that the $childNodes property of $oldNode is implicitly modified when $childNode is transferred from it to $newNode, so the internal pointer of $childNodes to the next child in the list is no longer accurate.

To get around this, I took advantage of the fact that any node with any child nodes will always have a $firstChild property pointing to the first one. The modified code that takes this approach is below and has the behavior I originally set out to implement.

while ($oldNode->firstChild) {
    $newNode->appendChild($oldNode->firstChild);
}

If you’re curious, below is the full code segment for renaming a node.

$newNode = $oldNode->ownerDocument->createElement('new_element_name');
if ($oldNode->attributes->length) {
    foreach ($oldNode->attributes as $attribute) {
        $newNode->setAttribute($attribute->nodeName, $attribute->nodeValue);
    }
}
while ($oldNode->firstChild) {
    $newNode->appendChild($oldNode->firstChild);
}
$oldNode->ownerDocument->replaceChild($newNode, $oldNode);

Another potential “gotcha” is the argument order of the replaceChild() method, which is the new node followed by the old node rather than the reverse that most people might expect. Thanks to Joshua May for pointing that one out to me; I might never have understood why I was getting a “Not Found Error” DOMException otherwise.

Splitting PHP Class Files

A recent work project required me to write a PHP script to interact with a remote SOAP service. Part of the service provider’s recommended practices entailed using a slightly dated software package called wsdl2php, which generates a single PHP file containing classes corresponding to all user-defined types from a specified WSDL file.

The issue I ran into was due to all the generated PHP classes being housed in a single file. I had to process two WSDL files that had several identical user-defined types in common. As a result, I couldn’t simply include the two PHP files generated from them because PHP doesn’t allow you to define two classes with the same name.

Looking at its source code, modifying wsdl2php to change this behavior was not a very appealing option. Attempting to consolidate the two WSDL files into one with no redundant user-defined type declarations seemed futile as well. Instead, I resolved to split the generated PHP files such that each class was contained in its own file. This would also allow me to use an autoloader to determine which of the classes I actually needed for the particular service call I was making.

Due to the number of classes, splitting the classes into separate files by hand would have been tedious and time-consuming. I decided to tap into my previous experience with the tokenizer extension to throw together a CLI script that would handle this for me. Once I got it working, it clocked in at just over 50 LOC with comments and whitespace. You simply call it from a shell and pass it the PHP file you want to split and the destination for the split class files.

I thought it might be useful for others needed to process similarly formatted source code, so I threw it into a github repository for anyone who might like to take a look. I’m open to suggestions for improvements to implement if enough people find it useful. Feel free to file an issue on the repository if you happen to find a bug.

So Long, Blue Parabola

I’ve decided to leave Blue Parabola. My last day there will be Tuesday February 16. I’d like to thank Keith Casey and Marco Tabini for choosing me to be part of the team. It’s been a privilege to work with them and I’ve learned a great deal.

As for what’s next, I’ll be starting at K-fx2. (Nope, no funemployment for me.) There, I’ll be developing Zend Framework applications and helping to streamline development processes and infrastructure.

Thanks to all my friends and family who’ve provided support during this transition. I’m looking forward to what the next year holds.

Speaking at a Conference

I can’t make any claim to the title of veteran conference speaker. Not yet, at least. However, I have done it once before at ZendCon in 2008 and I’ll be doing it again at php|tek this year. I thought I’d take a blog post to give out a few tips to any prospective first-time speakers based on my first speaking experience. I’m assuming there that you’ve already decided on a particular conference that you want to attend, you’ve submitted a session proposal, and you’ve been accepted.

First, in addition to the other things you should do before attending, be ready to give your presentation before you get on the plane. You should start on your slides as far in advance as possible. Don’t put it off or wait until the last minute, because it will likely be more work than you anticipate. This includes making sure that any live demos you intend to give will run as expected. Syntax errors and crashing web servers look very bad to the audience.

One of the reasons for this is that you’ll want to practice your talk out loud. It’s one thing to put the material onto slides, but it may sound different when it’s actually coming out of your mouth and going into the crowd. You may find stumbling points, places where you stutter or get caught off-guard when transitioning from one topic to another. Try to organize the presentation such that it matches your natural flow when talking about the topic without any slides at all.

Which reminds me, learn from the masters. People like Marco Tabini have spoken before and have a wealth of knowledge that they’ll share fairly freely most of the time, especially if alcohol (or, in Marco’s case, an espresso) is involved. Look at books like Presentation Zen by Garr Reynolds. Take the time to hone your presentation skills before you have to make your delivery.

If you’ve been to a conference before, you’ve probably already learned about my next point the hard way. Don’t depend on wifi internet access availability. Why not? Because the vast majority of the time, it will suck. There won’t be enough IP addresses, someone will do something to hog bandwidth and make latency skyrocket, it will find some way to refuse to work. Save local copies of files, write a minimal daemon to simulate a remote server, do whatever you need to do to avoid it.

That point goes hand in hand with this one: test your equipment early and have a Plan B. In particular, hook your laptop up to the projector in the room in which you’ll be speaking (or to a test projector, if the conference hosts provide one and prefer you use that) to make sure it can display your slides. Ben Ramsey was gracious enough to loan me his Macbook at ZendCon because my Sony Vaio refused to work with the projector and the time-sensitive situation did nothing but add to my speaking nerves. Make sure you don’t end up in the same spot.

Lastly, don’t let critical reception deter you from speaking again. I got pretty negative feedback the first time around, but I took it in stride. While I know I have plenty of room for improvement, I’m still going to give it another shot. Do your very best, then strive to be better.

Hope you enjoyed this blog post and gleaned something useful from it. If you’ve got any of your own speaking tips, please feel free to add a comment on this post. If you’ll be attending php|tek, I look forward to seeing you there!

Speaking at tek-X

As the recently released schedule shows, I will be speaking at the php|tek 2010 conference. The session I’ll be presenting is entitled “New SPL Features in PHP 5.3″ and will be an extended version of the webcast I presented as part of the CodeWorks webcast series.

While there, I also plan on participating in the Hack Track and may try to recruit a few new contributors (like you!) for the Phergie project. I am very much looking forward to the event and hope to see you there!

Database Testing with PHPUnit and MySQL

I recently made a contribution to the PHPUnit project that I thought I’d take a blog post to discuss. One of the extensions bundled with PHPUnit adds support for database testing. This extension was contributed by Mike Lively and is a port of the DbUnit extension for the JUnit Java unit testing framework. If you’re interested in learning more about database unit testing, check out this presentation by Sebastian Bergmann on the subject.

One of the major components of both extensions is the data set. Database unit tests involve loading a seed data set into a database, executing code that performs an operation on that data set such as deleting a record, and then checking the state of the data set to confirm that the operation had the desired effect. DbUnit supports multiple formats for seed data sets. The PHPUnit Database extension includes support for DbUnit’s XML and flat XML formats plus CSV format as well.

If you’re using MySQL as your database, CSV has been the only format supported by both the mysqldump utility and the PHPUnit Database extension up to this point. My contribution adds support for its XML format to the extension. While this support was developed to work in the PHPUnit 3.4.x branch, it won’t be available in a stable release until 3.5.0. In the meantime, this is how you can use it now.

  1. Go to the commit on Github and apply the additions and modifications included in it to your PHPUnit installation.
  2. From a shell, get your XML seed data set and store it in a location accessible to your unit test cases.
    mysqldump --xml -t -u username -p database > seed.xml
  3. Creat a test case class that extends PHPUnit_Extensions_Database_TestCase. Implement getConnection() and getDataSet() as per the documentation where the latter will include a method call to create the data set from the XML file as shown below.
    $dataSet = $this->createMySQLXMLDataSet('/path/to/seed.xml');
  4. At this point, you can execute operations on the database to get it to its expected state following a test, produce an XML dump of the database in that state, and then compare that dump to the actual database contents in a test method to confirm that the two are equal.
    $expected = $this->createMySQLXMLDataSet('/path/to/expected.xml');
    $actual = new PHPUnit_Extension_Database_DataSet_QueryDataSet($this->getConnection());
    // Specify a SELECT query as the 2nd parameter here to limit the data set, else the entire table is used
    $actual->addTable('tablename');
    $this->assertDataSetsEqual($expected, $actual);

That’s it! Hopefully this proves useful to someone else.

PHPUnit and Xdebug on Ubuntu Karmic

This is just a quick post to advise anyone who may be using PHPUnit and Xdebug together on Ubuntu Karmic. If you try to upgrade to PHPUnit 3.4.6 and you’re using the php5-xdebug Ubuntu package (which is Xdebug 2.0.4), you may get output that looks like this:

$ sudo pear upgrade phpunit/PHPUnit
Did not download optional dependencies: pear/Image_GraphViz, pear/Log, use --alldeps to download automatically
phpunit/PHPUnit can optionally use package "pear/Image_GraphViz" (version >= 1.2.1)
phpunit/PHPUnit can optionally use package "pear/Log"
phpunit/PHPUnit can optionally use PHP extension "pdo_sqlite"
phpunit/PHPUnit requires PHP extension "xdebug" (version >= 2.0.5), installed version is 2.0.4
No valid packages found
upgrade failed

There are two ways to deal with this situation. First off, note that the newer Xdebug 2.0.5 version includes several bugfixes including one related to code coverage reporting. That said, if you still want to continue using the php5-xdebug package anyway, you can force the upgrade by having the PEAR installer ignore dependencies like so:

sudo pear upgrade -n phpunit/PHPUnit

The other method involves installing Xdebug 2.0.5. First, if you have the php5-xdebug package, remove it.

sudo apt-get remove php5-xdebug

Next, use the PECL installer to install Xdebug. This requires that you have the php5-dev package installed so that the extension can be compiled locally.

sudo apt-get install php5-dev
sudo pecl install xdebug

At this point, create the file /etc/php5/conf.d/xdebug.ini if it doesn’t already exist and populate it with these contents:

zend_extension=/usr/lib/php5/20060613/xdebug.so

Then bounce Apache so that the new extension will be loaded.

sudo apache2ctl restart

That’s it. Hope someone finds this helpful.

I’m a Honey Pot

Side note: Yes, the title of this post is a throwback to the 418 status code in the HTTP protocol. My sense of humor is just odd that way.

I thought I’d kick things off on my new blog with a quick post on something I did while getting it set up.

Before switching to this new blog, I’d moved to using the spamhoneypot plugin on my old Habari blog to capture spam. I had a great amount of success in that switch, but in deciding to move to using WordPress on this new blog, I noticed that it had no equivalent plugins. There were several anti-spam plugins, but they all required use of a third-party service. I hadn’t seen consistent success with plugins that used this approach in the past, so I wanted to avoid repeating those experiences.

So, I decided to try my hand at writing a WordPress plugin. After wading through the filter and action documentation and googling around for a bit, I came up with a fairly simple plugin that seems to do the job.

The plugin works by adding a textarea field to the comment form that’s hidden using a CSS style. Since bots don’t generally detect CSS like this, they proceed to fill out the field like any other field. This implies that they aren’t a human being using a browser, in which case the plugin marks the comment as spam. I’ve found this catches the vast majority of spam comments with very few false results.

I’ve submitted to have the plugin hosted on the WordPress site, but until then, you can grab a copy off of a Github repository I’ve set up for it. Hope you find it useful!

Update 1/2/10 8:41 AM CST: The plugin is now available for download from the WordPress site.

Building PHP-GTK with Cairo Support on Ubuntu Jaunty

Elizabeth Smith managed to pique my interest and maintain the patience of Job long enough for me to successfully build PHP 5.3.0RC1 with PHP-GTK including Cairo support on Ubuntu Jaunty. The process was a bit arduous, as Ubuntu apparently has a rather “interesting” automake package, so I thought I’d document it here for anyone who might be interested in repeating the process.

I’m assuming here that you want to use as many available Ubuntu packages as is feasible, aside from maybe PHP itself, in order to minimize the amount of manual compilation necessary. To that end, there are a number packages you will need to install before getting started that do not come with a standard Jaunty installation.

sudo apt-get install subversion cvs libcairo2-dev libgtk2.0-dev

If you plan to build PHP from source, you will also need a few more packages.

sudo apt-get install build-essential autoconf libxml2-dev

Once you’ve got all the dependencies installed, the first step will be to grab a copy of PHP. You’ve got a few options in that regard.

Assuming you do a custom build, here’s how I did it.

./configure --with-gettext --disable-cgi --without-pear \
    --prefix=`pwd`/build/php_build
make
make install

Next, use Subversion to check out a copy of Cairo extension. If you did a custom PHP build, you can just place it on the same directory level as that.

svn co svn://whisky.macvicar.net/php-cairo cairo

At the present moment, the easiest way to install the Cairo extension is manually as a PECL extension. So, compile using the phpize utility in your PHP build.

cd cairo
../php-5.3.0RC1/build/php_build/bin/phpize
./configure --prefix=`pwd`/build/php_build \
    --with-php-config=../php-5.3.0RC1/build/php_build/bin/php-config

It’s at this point that Ubuntu’s “interesting” automake package comes into play. The Makefile generated by phpize will be missing a critical flag -DCOMPILE_DL_CAIRO in its CFLAGS setting value. Open the Makefile in any text editor and find the line that looks like this.

CFLAGS = -g -O2

Append the missing flag to the line so it looks like this, then save it.

CFLAGS = -g -O2 -DCOMPILE_DL_CAIRO

At that point, just continue the compilation process for the Cairo extension as normal.

make
make install

Now use CVS to check out a copy of the PHP-GTK extension. Place it on the same directory level as cairo.

export CVSROOT=:pserver:cvsread@cvs.php.net:/repository
cvs -q checkout -P php-gtk

If the phpize utility is not in your PATH, you’ll have to assign it to an environmental variable as those are the only two ways that the buildconf utility you’re about to use will pick it up.

export PHPIZE=../php-5.3.0RC1/build/php_build/bin/phpize

Execute the buildconf utility to generate the configure script, then execute it.

./buildconf
./configure --prefix=`pwd`/build/php_build \
    --with-php-config=../php-5.3.0RC1/build/php_build/bin/php-config

To have the PHP-GTK extension take advantage of the presence of the Cairo extension, you’ll need to add a flag to the CFLAGS setting in its Makefile. Open that, find the line that looks exactly like the original one modified in the Cairo Makefile, and append the flag -DHAVE_CAIRO to it so it looks like this.

CFLAGS = -g -O2 -DHAVE_CAIRO

At that point, continue the compilation process normally just as with the Cairo extension. Once that’s done, since the extensions were compiled as PECL extensions, you’ll need to enable them in your php.ini file.

If you did a custom build of PHP, just copy the php.ini-development file in the root of the extracted tarball directory to lib/php.ini within your build directory as this is where PHP will look for it by default. If you’re using PHP 5.3.0RC1, there is a syntax error around line 581 of that file. A URL should be commented out using a semicolon but isn’t. Note that the extension_dir setting needs to be set and, if you use a relative path, it must be relative to the current working directory from which PHP is invoked (the root PHP build directory in my case).

extension_dir = "lib/php/extensions/no-debug-non-zts-20090115"
extension=cairo.so
extension=php_gtk2.so

At this point, if you execute your php binary with the -m switch, you should get a list of extensions loaded. cairo and php-gtk should be among them and you shouldn’t see any errors before the extension listing. To take this for a test spin, there’s a particular demo file for PHP-GTK with Cairo support in the php-gtk checkout.

cd php-5.3.0RC1/build/php_build
bin/php php-gtk/demos/examples/cairo_support.php

If this works as expected, you should see a nifty little PHP-powered clock widget on your desktop.

Many thanks to Elizabeth for her help in putting this tutorial together and for all the very cool people working on the PHP-GTK project. You can find them in the #php-gtk channel on the Freenode IRC network. At the present time, some of them are in the process of revamping the PHP-GTK docs. In the meantime, you can check out the GTK docs for more current information.