PHP5.6+ cURL and file uploads

Came across something odd today, and thought I’d condense down my Tweets on the subject into a blog post.

Basically, I use cURL and some wacky wacky stuff to upload files to a site over HTTP POST. And since I’d just grabbed PHP 7 from HomeBrew, it had overridden my PHP 5.5 install that comes as standard on OSX 10.10 and thus I cross checked the script with my MacPorts PHP 5.6 install and found the same. (Yes THREE different PHP versions for science…)

“Traditionally” the method for this would be something along the lines of:

<?php

    $ch = curl_init('SOMEURL');
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36');

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $data = array(
        'some_file' => '@' . $some_path
    );
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

    $r = curl_exec($ch);
    curl_close($ch);

On PHP 5.5 and previous that works file, using a @ at the start of a POST entry would instruct PHP/cURL to treat the data/string as a File Path to Upload.

This behaviour is controlled by the PHP cURL constant of CURLOPT_SAFE_UPLOAD. In PHP 5.6 this constant changed from default FALSE to default TRUE, setting to TRUE means that a string starting @ is treated as a String and not a File Path to upload. The changes are documented on the PHP.net website, but the primary trip up is that most of us just use the defaults and we get tripped up when things change.

So, after trying to set this to FALSE under PHP 5.6 it still wasn’t working, and under PHP 7 you are thrown an error to indicate that you are not allowed to change this constant any more for security reasons, which is fine.

The solution is to use the CURLFile class, which is pretty straightforward:

<?php

    $ch = curl_init('SOMEURL');
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36');

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $data = array(
        'some_file' => new CURLFile($some_path)
    );
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

    $r = curl_exec($ch);
    curl_close($ch);

This is the truly lazy edition, just chuck a new CURLFile($path) at it, instead of the @. I’m sure CURLFile does more useful stuff, but this was enough to get me back up and running!

Thus endeth this blog post!

Curl, MusicBrainz and PHP

A quick block of PHP to fetch MusicBrainz ID for a song and artist based on Song Data.

As part of the New Years Resolutions I didn’t make.

I’m updating my blog with some Code Snippets!

Been fiddling about with my PHP powered Jukebox that plays out on LSRfm.com (Leeds Student Radio) Overnight, and there is a need for a bulk track adder.

So, I’ve been tidying up the MusicBrainz Data Fetcher, as well as fiddling with PHP and getting ID3 tags from Files. But thats a different blog post.

Essentially there is a (new-ish non updated recently) PHP Library for the interaction with MusicBrainz, (A music database), recently found that (this morning its rubbish), but of no good.

So went back to my curl method.

Tidyied it up and got it down to a few less lines.

Essentially for a given track, the PHP extracts the ID3 tags, and then passes it to this function: (its needs making into a function btw :-P)

$curl = new curl();

$data = array(
	'title'		=> 'Showdown',
	'artist'	=> 'Pendulum',
	'release'	=> 'In Silico',
	'duration'	=> 327784,
//	'tracknumber'	=> 0,
//	'count'		=> 10,
//	'releasetype'	=> '',

	'limit'		=> 25,
	'limit'		=> 1,
);
$target = 'http://musicbrainz.org/ws/1/track/?type=xml';
foreach ($data as $ref => $dat) {
	$target .= '&' . $ref . '=' . urlencode($dat);
}

                                $curl->target($target);
                                $curl->runit();
                                $mb = $curl->bodyarray['metadata'];

if (isset($mb['track-list'])) {
	$mb = $mb['track-list'];

	if (isset($mb['track']['0'])) {
		$mb = $mb['track'];
		$artist_id = $mb['0']['artist_attr']['id'];
		$song_id = $mb['0_attr']['id'];
	} else {
		$artist_id = $mb['track']['artist_attr']['id'];
		$song_id = $mb['track_attr']['id'];
	}
} else {
	// no data
}

echo "\n" . $artist_id . ' ' . $song_id;

Where new curl() just calls my Curl Class.

I’m sure you have your own ways of doing curl, but mine just sets the target with $curl->target and curlexec() with $curl->runit.

The result is in $curl->body, or exploded nicely as an array in $curl->bodyarray. I’ll post about my curl later! As well as some other Carlyon_CMS stuff.

So thats a quick rough and ready way to get a artist and song ID from MusicBraiz.

Its worth noting that since my limit is st to 1.

The first option of if (isset($mb[‘track’][‘0’]) is entirely redundant, as that only triggers when mb returns more than 1 result.

(I only just added limit before writing this post, which is where I decided to write this post)

The MusicBrainz Docs for its XML service is at: http://musicbrainz.org/doc/XML_Web_Service If your Interested!