Archive

Author Archive

Sending multiple values with the same name/key in cURL POST

August 7, 2011 Leave a comment

I came across this problem last Friday when I tried to talk to a third party API.

The problem:

  • the API use POST to send parameters
  • One of the parameter is designed to be able to accept multiple values with the same name/key.
  • One of the parameter is a file

In GET it could be represented as such : /api?name=James&name=Peter&name=Richard&…
In this example, we are sending 3 values for “name” (James, Peter and Richard), and this is perfectly valid since the API is expecting it and will process the value as it should.
The problem now lies with POST. In PHP cURL, a POST parameter is sent like this:

$data = array("name" => "James", "email" => "james@someone.com");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

Now we all know in an array, we only set the key once. Thus there is no way to send 3 different value for “name” in this case. So, how are we suppose to send ?name=James&name=Peter&name=Richard by POST?

An easy solution is to change the parameters into a string format, just like a GET URL (para1=val1&para2=val2&). Yes, PHP will accept both array or string.

$data = "name=James&name=Peter&name=Richard";
curl_setopt($ch, CURLOPT_POSTFIELDS, $data); // This will works

However, the problem comes when we need to send a file. According to PHP manual :

As of PHP 5.2.0, files thats passed to this option with the @ prefix 
must be in array form to work.

Which means if you need to send a file as one of your parameter, you MUST use the array format. What happens if the API needs a file, and also need multiple value for a name?

name=Peter&name=John&name=Richard&file=@text.csv

Clearly the string format won’t works because it needs to be an array if we want to send a file. And PHP array simply won’t allow multiple value for a single key. So what’s the solution?

After searching for a few hours, I came across this useful blog post that provide the solution:
http://blog.srcmvn.com/multiple-values-for-the-same-key-and-file-upl

The solution: we need to manually set the header as multipart/form-data, and set the boundar, and construct the content by ourselves.

This is the custom function. It will set both CURLOPT_HTTPHEADER and CURLOPT_POSTFIELDS options for us:

function curl_setopt_custom_postfields($ch, $postfields, $headers = null) {
    $algos = hash_algos();
    $hashAlgo = null;
    foreach ( array('sha1', 'md5') as $preferred ) {
        if ( in_array($preferred, $algos) ) {
            $hashAlgo = $preferred;
            break;
        }
    }
    if ( $hashAlgo === null ) { list($hashAlgo) = $algos; }
    $boundary =
        '----------------------------' .
        substr(hash($hashAlgo, 'cURL-php-multiple-value-same-key-support' . microtime()), 0, 12);

    $body = array();
    $crlf = "\r\n";
    $fields = array();
    foreach ( $postfields as $key => $value ) {
        if ( is_array($value) ) {
            foreach ( $value as $v ) {
                $fields[] = array($key, $v);
            }
        } else {
            $fields[] = array($key, $value);
        }
    }
    foreach ( $fields as $field ) {
        list($key, $value) = $field;
        if ( strpos($value, '@') === 0 ) {
            preg_match('/^@(.*?)$/', $value, $matches);
            list($dummy, $filename) = $matches;
            $body[] = '--' . $boundary;
            $body[] = 'Content-Disposition: form-data; name="' . $key . '"; filename="' . basename($filename) . '"';
            $body[] = 'Content-Type: application/octet-stream';
            $body[] = '';
            $body[] = file_get_contents($filename);
        } else {
            $body[] = '--' . $boundary;
            $body[] = 'Content-Disposition: form-data; name="' . $key . '"';
            $body[] = '';
            $body[] = $value;
        }
    }
    $body[] = '--' . $boundary . '--';
    $body[] = '';
    $contentType = 'multipart/form-data; boundary=' . $boundary;
    $content = join($crlf, $body);
    $contentLength = strlen($content);

    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Length: ' . $contentLength,
        'Expect: 100-continue',
        'Content-Type: ' . $contentType,
    ));

    curl_setopt($ch, CURLOPT_POSTFIELDS, $content);

}
Here is how we use this function:
curl_setopt_custom_postfields($ch, array(
    'file' => '@a.csv',
    'name' => array('James', 'Peter', 'Richard'),
));

A closer look at the header shows that we are setting the boundary as “Content-Type: multipart/form-data; boundary=—————————-37d7f9e41a21″. All the content sent will be separated by the boundary :

------------------------------37d7f9e41a21
Content-Disposition: form-data; name="file"; filename="a.csv"
Content-Type: application/octet-stream

123, 456
------------------------------37d7f9e41a21
Content-Disposition: form-data; name="name"

John
------------------------------37d7f9e41a21
Content-Disposition: form-data; name="name"

Peter
------------------------------e6cf9faddfbf
Content-Disposition: form-data; name="name"

Richard
------------------------------e6cf9faddfbf--

That’s it. Now we can send file and multiple values with a single name in POST. I sure hope PHP comes up with a solution in the near future.

Categories: Programming

The best of 2011 so far – Rise of the Planet of the Apes

August 7, 2011 Leave a comment

 

This film caught my attention few months ago when I saw James Franco and Andy Serkis attached to it. Sure, James Franco might not be a good Oscar host, but he is a still good actor and I believe he does mind the script he picked. So if he chooses to involve in a prequel of a 2001 remake of a 1968 film, there must be a good reason behind it 🙂

Another reason is Andy. His performance in The Lord of the Ring was nothing short of genius. Even in the mediocre King Kong he gave us his best. (I also found his role as  a thug in The Cottage mildly amusing). Combining these together and I have pretty high expectation for the film. Not to mention the good ratings from Rotten Tomatoes.

Finally I watched it on Friday night, and it was one of the most satisfying experience i had since Inception. Again Andy’s portrayal of Ceasar is the highlight of the film. His transition from a cute happy little chimp to a confused, sad adult being locked up in a zoo, and later the brave, revolutionary leader of the groups is truely amazing. The scene where he retaliate against the cruel zoo keeper (Tom felton who also played Draco Malfoy in H.P) is one of the most powerful scene I’ve watched recently.

James Franco did okay. I particularly like the sub-plot where he tried so hard to find a cure for his dad with Alzheimer’s disease, only to realize that something aren’t meant to be changed. Another milestone for you, James. Keep up the good work and stay away from hosting 🙂

The final fight scene is very well-crafted. Again it proves that a lower budget doesn’t really deteriorate the quality of a movie. I find it even more engaging and entertainment than most action movies in 2011.

Overall, a very good movie. Let’s hope it gets the attention it deserve and rule the box office!

 

Categories: Movies

Testing_Selenium 0.4.4 runs extremely slow…

July 24, 2011 Leave a comment

There’s a bug in Testing_Selenium version 0.4.4 that causes the test cases to run  extremely slow. A common navigate/click action takes minutes to complete. This is a known bug and has been reported in the official website.

For those who doesn’t need to instantiate the selenium obj manually, using PHPUnit_Extensions_SeleniumTestCase from PHPUnit seems to be a better option.  However if you can’t extend your class directly and need to manually instantiate the selenium object, the temporary solution is to downgrade to 0.4.3 by pear install Testing_Selenium-0.4.3, or download the .tgz file from the official website and install manually by pear install path_to_file.tgz.

 

Categories: Programming

“What is a ghost?” – The Devil’s Backbone

July 19, 2011 Leave a comment

Guillermo del Toro sure knows how to make good ghost movies. This is one of his work at 2001, way before he made Hellboy, The Orphanage and Pan’s Labyrinth (one of my all-time favourite!). A drama/thriller set in Spanish Civil War era, the film tells the story of a boy who recently lost his father and was subsequently placed under care in an orphanage. Not a very good place to be at that time, he found himself constantly bullied by other kids, and frequently visited by a troubled spirit of a dead child….

Unlike most Hollywood horror movies (which only uses sound effect and cheap scares), Guillermo’s film rely heavily on story and atmosphere. If you enjoy films like the Orphanage or Pan’s Labyrinth, you will definitely enjoy this one as well. The place is creepy, the cinematography is great, and all the characters are well portrayed. In fact the main antagonist is not the ghost but the cruel adults that terrorize the children.

Overall, a well-crafted film by one of my favorite director. I still think Pan’s Labyrinth is his best, but this is definitely better than most of the main-stream Hollywood horror movie. If you enjoy slow pace ghost story with rich characters good story, then you’ll definitely love this!

Categories: Movies

A compilation of common PEAR errors

July 19, 2011 Leave a comment

I have been using PHP PEAR a lot lately to add new packages into my local Windows machine. Here are some common error messages I encountered and their respective solution. Hope this could help other aspiring Windows PHP programmers:

 

Error:
package/somepackage requires PHP extension “some_extension”Solution:
Enable the extension in your php.ini. Edit php.ini and find the line that contains extension=the_extension.dll, and un-comment it (you might need to set the full path or define the include folder)
Error:
Unknown remote channel: <some channel>Solution:
This is the most common error possibly, and a no brainer. Just use pear channel-discover <some channel> to add this channel.
Error:
No release available for package <some package>Solution:
Most probably the channel information is outdated. Try pear channel-update <channel-name> or pear upgrade-all. If it still fails, try updating PEAR version by pear channel-update pear.php.net. If the error still occurs, try re-installing PEAR and update it to an older version by pear upgrade PEAR-1.9.X. Also we can try to manually download the .tgz file and use pear install <path to tgz>
Error:
Error getting channel info from <some channel>. SECURITY ERROR: Will not write to <some folder> as it is symlinked to <some folder> – Possible symlink attackSolution:
Delete everything in your temp folder (this usually happens to Windows user only. Never seen this in Ubuntu). You can get a hint of the temp folder from the error message itself. Usually it’s located at Documents and Settings\user\Local Settings\Temp\pear\cache
Error:
package/package requires PEAR Installer (version >= 1.9.X), installed version is 1.9.XSolution:
We need to update PEAR to the required version. Use pear upgrade PEAR-1.9.X, or pear channel-update pear.php.net follow by pear upgrade-all to upgrade to the latest version.
Error:
Failed to download <package>/<package> within preferred state “stable”, latest release is version X.X.XRCX, stability “beta”, use “channel://<some url>” to install.Solution:
When a required package is still in non-stable release, PEAR will not automatically download them. We need to explicitly download the unstable version by pear install channe://<some url> before we can proceed with the main package.

 

That’s about all the errors I’ve got so far. Once we get over the learning curve, PEAR is actually a pretty neat tool. Happy PEAR-ing!

Categories: Programming

The Disappearance of Alice Creed – Three people is all you need

July 17, 2011 Leave a comment

The Disappearance of Alice Creed

 

I watched “The Disappearance of Alice Creed” last night and I love it! It shows us that to make a good film, all you need are :

  1. A good script
  2. Three dedicated actor/actress
  3. A room
  4. A director that knows how to tell a good story

Gemma Arterton (Clash of the Titan, Prince of Persia) was brilliant in it, and the other two male kidnapper also carry their roles very well (be warned, there are nude and man-2-man kissing scene 🙂 ) The pacing was really good and there were plot-twist to get you interested from the beginning till the end.

Definitely one of the best “kidnapping” film I’ve watched recently. Highly recommended for crime-thriller fans!

Categories: Movies

Installing PHP5.3 (along with PEAR and PHPUnit), Apache 2.2 on Windows XP

July 16, 2011 Leave a comment

It’s been few years back since I last installed a fresh copy of PHP + Apache on Windows, partly because PHP 5.2 (My last attempt) works fine for me. Recently I need to setup Behat which requires PHP 5.3, so I tried it on a fresh Windows XP virtual PC image.

With my previous experience I thought getting PHP5.3 will be a somewhat smooth and easy process. How wrong was I 🙂 Apparently PHP and Apache have branched their window release to a different website : PHP at PHP For Windows and Apache at Apache Lounge. You will get the much required “php5apache2_2.dll” only if you get php package from here instead of old php.net.

To be honest this is the first time I come across PHP For Windows. Previously I always get my php package from php.net. From the website I notice now PHP is separated into VC6 and VC9 build. PHP5.3 is only available in VC9 version, and for that you will need to use Apache VC9 too. The standard Apache installer from apache.org simply will not work with it, so now we will need to fetch apache from Apache Lounge.

To be honest I didn’t read the instruction carefully in my first go, hence all the headache I got when I tried to install PHP/Apache from the official websites. I spent a good few hours to figure out what the hell is wrong, and finally realize I was getting the wrong installers. So here are the proper ways to install PHP5.3 + Apache2.2:

  • First we need Microsoft 2008 C++ runtime in order to run VC9 version of php/apache.
  • Download Apache from Apache Lounge. Grab the latest zip file (it should have a name like httpd-2.2.X-win32-x86-ssl.zip). Extract it to c:\apache2.
  • Open cmd and run C:\apache2\bin\httpd.exe -k install. This will install apache service. If all went well you should be able to start/stop the service using C:\apache2\bin\ApacheMonitor.exe. To test apache, go to http://localhost with a browser and you should see the famous “It works!” printed out.
  • Download php 5.3 VC9 from PHP For Windows. DO NOT take the MSI version as it doesn’t contain the crucial php5apache2_2.dll. Take the zip version instead. The one I got was php-5.3-ts-windows-vc9-x86-r312319.zip.
  • Extract it to a folder of your choice (I put c:\php as my installation folder, following old convention). Find php.ini-development and rename it to php.ini.
  • Define your timezone in php.ini (this is introduced in php 5.3. You will get a PHP warning (“It is not safe to rely on the system’s timezone settings”) if you fail to set this value. You can look for the list of timezone here. To set the timezone, locate the line with “;date.timezone” in php.ini,  and change it to something like this: date.timezone = “Asia/Kuala_Lumpur”
  • Launch cmd.exe and try executing c:\php\php -v. You should see the version of PHP printed nicely.
  • Edit Apache configuration file to load PHP. Add these line at the end of the file. Be sure to change them to the actual PHP installation directory if you opt not to install in C:\php.
#
LoadModule php5_module "c:/php/php5apache2_2.dll"
AddHandler application/x-httpd-php .php

# configure the path to php.ini
PHPIniDir "C:/php"
  • Restart apache service and PHP should be up-and-running. Create some php file in C:\Apache2\htdocs and it should be working as expected.

With that being done, now it’s time to install PEAR and get my copy of PHPUnit (test first, code later!!).

  • Open C:\php\go-pear.bat, and change PHP_BIN to the absolute path to php.exe in (set PHP_BIN=C:\php\php.exe)
  • Launch cmd and run C:\php\go-pear.bat
  • Keep on pressing “enter” until it finishes to install and you should see “For convenience, a REG file is available under…”
  • You should now see a newly generated file “PEAR_ENV.reg” in C:\php. Double click this file to register PEAR-related environment variables.
  • To test if PEAR is there, run pear -V in command prompt. Voila! You should now see both PEAR and PHP version (in my case they are 1.9.1 and 5.3.6 respectively)

Pear is now at your service. Next we will try to install PHPUnit with our new shiny pear. Launch command prompt and run these commands:

  • pear channel-discover pear.phpunit.de
  • pear channel-discover pear.symfony-project.com
  • pear channel-discover components.ez.no
  • pear upgrade-all

Now try to see your pear version again with pear -V. Some of the components of PHPUnit require at least version 1.9.2 or PEAR. The last command (pear upgrade-all) should upgrade our PEAR to 1.9.4.

Next run pear install phpunit/PHPUnit to see if you can install phpunit successfully. Sometimes you might bump into a few error messages such as:
  • “Failed to download XXX within preferred state “stable”…. use “channel://XXX” to install” – The problem is that when a required component is yet to have a stable release, PEAR will NOT automatically download them, but instead prompt you with a this message. All you need to do is manually install these component with pear install channel://XXX command. In my case I need to manually download HTTP_Request2-2.0.0RC1a (pear install channel://pear.php.net/HTTP_Request2-2.0.0RC1) and Net_URL2-0.3.1 (pear install channel://pear.php.net/Net_URL2-0.3.1). Hopefully stable version of these components will be released in the near future.
  • XXX requires PEAR Installer (version >= 1.9.X), installed version is 1.9.X – Obviously this means your PEAR isn’t update to the version needed by some of the components. Try to update your PEAR using either pear upgrade-all, pear channel-update pear.php.net or pear upgrade PEAR-1.9.X until you get the correct version.
  • Unknown remote channel: XXX – You need to use channel-discover XXX to “discover” this channel.
  • Error getting channel info from XXX. SECURITY ERROR: Will not write to <cache folder> as it is symlinked to XXXX – Possible symlink attack – I encounter this error once. The solution was to delete all content from the pear tmp cache folder. It’s usually located at Documents and Settings\<your username>\Local Settings\Temp\pear\cache

If all goes well, you should see “install ok: channel://pear.phpunit.de/PHPUnit-3.5.14” at the end of the installation process. Now try to run

phpunit --version 

in command prompt…. (the moment of truth)…. and here comes the message PHPUnit 3.5.14 by Sebastian Bergmann” !

That’s it. You have successfully installed Apache + PHP 5.3 + Pear + Phpunit in your machine! One last piece of advice: If you’ve tried all the above, google online for as long as you can, and still fail to get them up and running, it’s time to switch to other OS like Ubuntu or Fedora. Things are so much easier there 🙂

Categories: Programming