Search code examples
phpapitwitterencodingtweets

PHP - Twitter API - Request Corrupts When Using Spaces


I'm working on a script that utilizes the Twitter API to post a tweet. It's for a very minimal service so I chose to utilize with a single function rather than using an entire library.

This is what the function looks like:

function postTweet($content, $method = 'POST') {
    $consumer_key = '';
    $consumer_secret = '';
    $access_token = '';
    $access_secret = '';
    
    $nonce = md5(mt_rand(1, 10000000));
    $tstamp = time();
    
    $uri = 'https://api.twitter.com/1.1/statuses/update.json';
    $data = array('status' => $content);
    
    $ch = curl_init();
    $parameters = [
        "oauth_consumer_key" => $consumer_key,
        "oauth_nonce" => $nonce,
        "oauth_signature_method" => "HMAC-SHA1",
        "oauth_timestamp" => $tstamp,
        "oauth_token" => $access_token,
        "oauth_version" => "1.0"
    ];
    
    $base = $method.'&'.rawurlencode($uri).'&';
    $parameters = array_merge($parameters, $data);
    
    array_map('rawurlencode', $parameters);
    array_map('rawurlencode', array_keys($parameters));
    
    $pstring = '';
    foreach ($parameters as $key => $value) {
        $pstring .= sprintf("%s=%s&", $key, $value);
    }
    
    $pstring = substr($pstring, 0, strlen($pstring) - 1);
    $base .= rawurlencode($pstring);
    $signingKey = rawurlencode($consumer_secret).'&'.rawurlencode($access_secret);
    
    $signature = base64_encode(hash_hmac('sha1', $base, $signingKey, true));
    
    $options = [
        CURLOPT_URL => $uri,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            sprintf('Authorization: OAuth oauth_consumer_key="%s", oauth_nonce="%s", oauth_signature="%s", oauth_signature_method="HMAC-SHA1", oauth_timestamp="%d", oauth_token="%s", oauth_version="1.0"', $consumer_key, $nonce, rawurlencode($signature), $tstamp, $access_token)
        ]
    ];
    
    curl_setopt_array($ch, $options);
    
    if($method === 'POST') {
        curl_setopt($ch, CURLOPT_POST, true);
    }
    
    if ($data !== null && $method == 'POST') {
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
    }
    
    return json_decode(curl_exec($ch), true);
}

And it works, but only for a single word. Testing! passes and is being posted on Twitter, while Testing testing! does not and Twitter returns error 32 "Could not authenticate you.".

I think this is some kind of encoding issue, otherwise it wouldn't post the word. I've tried adding urlencode and rawurlencode to $content, which did not result in success.

What could be the problem?


Solution

  • The problem comes from these lines:

    array_map('rawurlencode', $parameters);
    array_map('rawurlencode', array_keys($parameters));
    

    They have no effect because you don't do anything with the return values. I suggest removing them and doing this instead:

    $pstring = '';
    foreach ($parameters as $key => $value) {
        $pstring .= sprintf("%s=%s&", rawurlencode($key), rawurlencode($value));
    }