Sunday 3 July 2011

PHPUnit unable to run all files in a directory in Windows

It's been a while since I have worked with a windows environment, and unfortunately stuff that's built for unix (ie. PHPUnit) doesn't play very nicely with a Windows environment. Take for example, the ability to run all the tests in a directory. In Ubuntu, I just had to type:

#phpunit test

to run all my tests in the the test folder. However, when I do this in windows, phpunit did not run. At first I thought it had to do with the way the path seperators are different between Windows / Unix, but I found out that is actually NOT the reason. It turns out that phpunit needs Xdebug (http://xdebug.org/download.php) in order for it to run multiple tests at the same time. So if you're a Windows user and wish to run multiple tests, download Xdebug first.

Wednesday 22 June 2011

Cannot restart apache in Ubunbu

I have issues from time to time with this command:

/etc/init.d/apache2 restart

I have even tried to kill the process with a ps -9 and still no luck.

I finally figured it out:

sudo /usr/sbin/apache2ctl restart

Saturday 18 June 2011

Reflection on using PHP's SoapClient

One of the great things about using PHP is the sheer number of built in functions (BIFs). Unfortunately though, some of the functions are not documented that well. Take the SoapClient class for example. I just spent a few hours today trying to configure it, so I'm posting up my experience so hopefully others don't have to spent those hours.

The problem:

I am trying to connect to a web service for a third party, and here is the bit of the wsdl that is interesting:

<wsdl:definitions targetNamespace="http://questionmark.com/QMWISe/">
...
<s:element name="Security" type="tns:SecurityHeader"/><s:complexType name="SecurityHeader"><s:sequence><s:element minOccurs="0" maxOccurs="1" name="ClientID" type="s:string"/><s:element minOccurs="0" maxOccurs="1" name="Checksum" type="s:string"/></s:sequence><s:anyAttribute/></s:complexType>
...
</wsdl>

When I make a call, I have to construct the header according to this:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:qmw="http://questionmark.com/QMWISe/">
   <soap:Header>
      <qmw:Security>
         <!--Optional:-->
         <qmw:ClientID>id</qmw:ClientID>
         <!--Optional:-->
         <qmw:Checksum>hash</qmw:Checksum>
      </qmw:Security>
   </soap:Header>
   <soap:Body>
       ...
   </soap:Body>
</soap:Envelope>

Simply enough right? Well not with the way SoapClient is structured.

This was what I had initially:

In client code:
 $this->client = new SoapClient(null, $this->constructParams());
 $this->client->__setSoapHeaders(new SoapHeader($this->targetNamespace, "Security",
            new SoapVar(new Security($this->username, $this->getMd5Hash()), SOAP_ENC_OBJECT));

In class Security:
class Security {

    function Security($clientId, $md5Hash) {
        $this->ClientID = $clientId;
        $this->Checksum = $md5Hash;
    }
}

Since we're using the SOAP_ENC_OBJECT flag which will tell PHP to use the variable name of the Security object, everything should be good to go right? Wrong. This is what it spits out:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://questionmark.com/QMWISe/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:enc="http://www.w3.org/2003/05/soap-encoding">
      <env:Header>
            <ns1:Security>
                  <ClientID>id</ClientID>
                  <Checksum>hash</Checksum>
            </ns1:Security>
      </env:Header>
      <env:Body>
           ...
      </env:Body>
</env:Envelope>

Notice the difference? It's missing the namespace declaration for <ClientID> and <Checksum>, which leads to web service server complaining when we send out the request.


The solution:

So rather than using the docs from PHP, this is what I did to get around the issue:

new SoapHeader($this->targetNamespace, "Security",
            new SoapVar(array("ns1:ClientID" => $this->username, "ns1:Checksum" => $this->getMd5Hash()), SOAP_ENC_OBJECT));

The only issue here is that we are coding the ns1 namespace in the parameters. Although the namespace prefix won't change it just annoys me that I have to introduce this workaround in there to fix the issue. This is a workaround because it appears there is a PHP bug filed for this misbehavior:

When we do that, we see the correct behavior:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://questionmark.com/QMWISe/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:enc="http://www.w3.org/2003/05/soap-encoding">
      <env:Header>
            <ns1:Security>
                  <ns1:ClientID>id</ns1:ClientID>
                  <ns1:Checksum>hash</ns1:Checksum>
            </ns1:Security>
      </env:Header>
      <env:Body>
         ...
      </env:Body>
</env:Envelope>

Problem with PHP's SoapClient and the hash symbol

I am writing an integration piece with a Learning Management System (LMS) called QuestionMark, and I was using PHP's out of the box SoapClient to retrieve information from a web service, but I kept getting this message after checking all my other parameters were correct:

Server did not recognize the value of HTTP Header SOAPAction: http://questionmark.com/QMWISe/#GetParticipant.

It turns out that I needed to specify the correct version of SOAP in order for this to work. So to fix this issue, add this to the array while constructing the SoapClient object:

'soap_version' => SOAP_1_2

More context:

$params = array('location' => $this->wsdlUrl, 'uri' => $this->targetNamespace, 'soap_version' => SOAP_1_2, 'trace' => 1)

$this->client = new SoapClient(null, $params);

Sunday 5 June 2011

FIXED: CUPS print server unable to start

Stranglely I started having problems with the CUPS print server when I installed Zend Server and Zend Studio on my Ubuntu 11.04 system. This was what the logs were showing:


Jun  3 10:36:29 acjcm3c kernel: [  527.087977] type=1400 audit(1307122589.944:318): apparmor="DENIED" operation="file_mmap" parent=1 profile="/usr/sbin/cupsd" name="/usr/local/zend/lib/liblber-2.4.so.2" pid=5107 comm="cupsd" requested_mask="m" denied_mask="m" fsuid=0 ouid=0
Jun  3 10:36:35 acjcm3c kernel: [  532.130427] type=1400 audit(1307122594.994:319): apparmor="STATUS" operation="profile_replace" name="/usr/lib/cups/backend/cups-pdf" pid=5135 comm="apparmor_parser"
Jun  3 10:36:35 acjcm3c kernel: [  532.132847] type=1400 audit(1307122594.994:320): apparmor="STATUS" operation="profile_replace" name="/usr/sbin/cupsd" pid=5135 comm="apparmor_parser"
Jun  3 10:36:35 acjcm3c init: cups main process (5136) terminated with status 127
Jun  3 10:36:35 acjcm3c init: cups main process ended, respawning

The solution:

It appears that Zend has put some of its libraries in the common dir which loads all the Zend related so's.

Just do these four commands:

cd /usr/local/zend/lib
sudo rm libldap_r-2.4.so.2
sudo rm libldap-2.4.so.2
sudo rm liblber-2.4.so.2

Tuesday 31 May 2011

PHP FIXED: mysql_error() 2002: Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

If you are seeing the error above while running PHPUnit in Zend Studio, then I suggest you follow what I do. Here is my setup by the way: Ubuntu 11.04, Mysql 5.1.54.

Solution:

Just make a symbolic link from your mysql.sock to the directory it is looking:

ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock

Friday 27 May 2011

HOW-TO: get Kraft working in Ubuntu 11.04

I was trying to set up a small business application called Kraft in Ubuntu. Everything installs fine, but when I tried to create a customer it doesn't allow me to select an address book. To all those out there who are struggling with this and can't go any further in the app, there is a fix for this. Apparently Kraft is based on KDE and it is expecting the app KAddressBook to be installed already. Problem is, Ubuntu doesn't come bundled with that initially and you have to get it yourself.

So open Ubuntu Software Center, and search for KAddressBook, download it, and restart Kraft. It should now work!

Wednesday 25 May 2011

PHP required_once(), include_once() errors

So I'm writing a Object Oriented webapp in PHP, and I have discovered a problem that hopefully the solution I found is useful to someone.

The Problem:

I have a front controller that delegates its tasks to a Service class, and that Service class has a few references to the domain objects. Here is my folder structure:

/var/www:
frontController.php
/var/www/service:
FruitService.php
/var/www/domain:
Apple.php
Orange.php  

Now in my FruitService.php, I originally had a couple of require_once() like so:

require_once('../domain/Apple.php');
require_once('../domain/Orange.php');

Seems reasonable right? WRONG! I spend about an hour trying to see why PHP fails with this nasty message:

PHP Warning:  require_once(../domain/Question.php) [<a href='function.require-once'>function.require-once</a>]: failed to open stream: No such file or directory in FruitService.php on line 2

PHP Warning:  require_once(../domain/Question.php) [<a href='function.require-once'>function.require-once</a>]: failed to open stream: No such file or directory in FruitService.php on line 2
It turns out that since your controller is serving the page, all the paths would have to be relative to that location of the controller. So in my example, this would work if I were to put it in FruitController.php:

require_once('domain/Apple.php');
require_once('domain/Banana.php');

However, this is a really bad idea since if you have another controller that uses the FruitController then your include path will break;

The formal solution would be to use the varible $_SERVER['DOCUMENT_ROOT'], so it would look like this:

require_once($_SERVER['DOCUMENT_ROOT'] . '/domain/Apple.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/domain/Apple.php');

Monday 23 May 2011

New shortcut in Ubuntu 11.04 to open a terminal

I had an interesting read about Ubuntu 11.04 and Unity located here http://askubuntu.com/questions/28086/unity-keyboard-mouse-shortcuts, and there is a pretty cool shortcut to open the terminal from anywhere.

Ctrl + Alt + T

will open a new Terminal basically from anywhere. Pretty cool. The only downsize is that Unity maps some of the common keystrokes that Eclipse also uses. I wish there was a way to configure Unity's keystrokes.

Saturday 21 May 2011

Problem with Ubuntu 11.04 when installing packages

If you are seeing something like this when installing / updating software:

Setting up tzdata (2011g-0ubuntu0.11.04) ...
Use of uninitialized value $reply in scalar chomp at /usr/share/perl5/Debconf/FrontEnd/Passthrough.pm line 66.
Use of uninitialized value $reply in concatenation (.) or string at /usr/share/perl5/Debconf/FrontEnd/Passthrough.pm line 67.
Use of uninitialized value $reply in split at /usr/share/perl5/Debconf/FrontEnd/Passthrough.pm line 68.
Use of uninitialized value $reply in scalar chomp at /usr/share/perl5/Debconf/FrontEnd/Passthrough.pm line 66.
Use of uninitialized value $reply in concatenation (.) or string at /usr/share/perl5/Debconf/FrontEnd/Passthrough.pm line 67.
Use of uninitialized value $reply in split at /usr/share/perl5/Debconf/FrontEnd/Passthrough.pm line 68.
Use of uninitialized value $reply in scalar chomp at /usr/share/perl5/Debconf/FrontEnd/Passthrough.pm line 66.
Use of uninitialized value $reply in concatenation (.) or string at /usr/share/perl5/Debconf/FrontEnd/Passthrough.pm line 67.
Use of uninitialized value $reply in split at /usr/share/perl5/Debconf/FrontEnd/Passthrough.pm line 68.
Use of uninitialized value $ret in string eq at /usr/share/perl5/Debconf/FrontEnd/Passthrough.pm line 109.
dpkg: error processing tzdata (--configure):

There is a solution that I did to fix this is to run this command in the command prompt:

 sudo dpkg-reconfigure tzdata

then follow the instructions to set the time zone. For some reason the Los Angeles timezone did not work out for me so I had to set it to the New York timezone. Then retry to to install the package and voila!