Here’s how I literally wasted eight hours of my life. :-)
We signed up for Yahoo! Search Boss last week. The process itself was pretty straight:
- Sign into your Yahoo! account at https://developer.apps.yahoo.com/
- Click “New Project”, fill out the form.
- Then click on the project name, activate “Yahoo! Search Boss” by suppling some billing info.
Consumer key rejected?
The above process doesn’t even take five minutes, but then I spent eight hours figuring out what oauth_problem=consumer_key_rejected
means. Spent a couple hours googling, reading bug reports and even posted to the Yahoo! group associated with Search Boss.
To cut to the chase: When you create a new project, it’s not sufficient to just activate “Yahoo! Search Boss” (and provide billing details and so on).
You have to select another (pointless) API along with it. Even if you don’t use it. Apparently a consumer key and secret are only put into use when you selected one of their other offerings. For some reason, Yahoo! Search Boss doesn’t work by itself.
So once I selected one of their other APIs (I went for Knowledge Plus), I copied my updated consumer secret, it magically worked.
Add to all of the above the constant link screw up in Yahoo!’s developer documentation. Since there’s a v1 and a v2 of the API you’re bound to find the wrong one for sure — the best way to find it: Google.
Small working example for request tokens?
Sure, so to get a request token, this is what you do with Zend_Oauth
:
$config
is an instance of Zend_Config
. In addition to the oauth.ini
later in the blog post, you’ll also need requestTokenUrl
to retrieve a request token.
The result ($token
) is of Zend_Oauth_Token_Request
.
Nice to know: In the process I discovered that in case an access token was needed, I had to make sure that $token
contains oauth_verifier
. This verifier is only supplied when you supply a callbackUrl
(in your ini file). If you supply oob
(out of bounds — no callback), the verifier has to be entered by the user manually later on.
Consumers and Tokens
And the best part of this discovery is, that the Search Boss API doesn’t use any OAuth tokens at all.
I guess I must have overread that in the process and only figured it out when I started looking at the sample OAuth code, or rather stepped through it all to figure out what it is doing (and what I was doing wrong). (More on that later!)
But anyway, I’m not afraid to learn new things.
Two for the price of one!
So once I mastered the credentials, it lead me to another thing: OAuth implementations in PHP are pretty sucky, and PHP doesn’t seem to be alone here either. :-) And even if the implementation is not so sucky, their documentation usually is and real life examples are rather sparse.
So for example, I honestly don’t know why developers who build OAuth wrappers rather explain OAuth for the who knows how many time, instead of providing example code. Example code goes far, and for reading yada yada just link to oauth.net. :-)
In case of the Zend Framework (and all things Zend_Oauth
), I used the components unit tests to figure out a direction though that didn’t take me too far.
Da codez.
In the end, I wrapped OAuth.php
into a unit test and wrote some code using Zend_Oauth_Http_Utility
until the requests looked the same. Here’s what I ended up with!
oauth.ini
For simplicity, I’m not showing [testing]
etc..
Nice to know: In case you’re wondering how I came up with all the names, have a look into Zend_Oauth_Config::setOptions()
. I particulary enjoyed looking for that one via an undocumented Zend_Oauth_Client::__call()
. If you were wondering what @method
is for in phpdoc, this is exactly it. ;-)
Make it all work!
So first off, we read the configuration file and push it all into an array $oauthParams
.
We also initialize another array called $query
(which is later appended to the URL).
Last but not least, we define $url
, which is the address of the Search Boss endpoint.
Both arrays are combined into $params
for signature creation:
Then the signature is added to $oauthParams
and we’ll convert the array into an Authorization
header:
Last but not least, we do the request!
First we assemble the complete URL, then we assign all the variables to the $client
object and finally do a request!
If all went well $response
contains an instance of Zend_Http_Response
.
I hope I didn’t forget anything, but it should be relatively straight-forward to wrap this into a lightweight object oriented wrapper.
Other pointers:
- always evaluate the status code (hint: REST-API)
- use try/catch
Fin
That’s all. Sure hope this saves someone else some time.