I keep hesitating to call it an “API”, because I’m pretty sure this isn’t quite what “API” has come to mean. But, it is programming that interfaces with an application, so whatever. Last week I introduced the bit of JavaScript you can include on your page to add a random design quote to your page from Quotes on Design. Today, we’ll go over how it was built and how it works. Thanks again to David Walsh for helping me create it.
How does it work?
I really wanted to make sure that it was as easy as possible to use, which basically meant it had to be a “snippet”. It needed to be JavaScript for sure, as that gives us the power to alter the DOM and insert the quote as needed. At first, I went fully JavaScript. I had it read the sites RSS output and insert one of those at random. It turned out to be painfully slow, required a JavaScript library, and was limited to the last 10 quotes added.
David suggested making the JavaScript file actually a PHP file. Wha?
Tricky Tricky – The JavaScript is really PHP
Ultimately the JavaScript needs to really be JavaScript. But, we can gather the information we need first with PHP and then dynamically echo out what we need. You can see this in action if you load the JS file directly.
Make the server see it as PHP
First we need to make sure the server interprets the file as PHP, despite it’s .js file extension. We do this with a little HTAccess magic:
<FilesMatch "^.*?api.*?$">
SetHandler php-script
</FilesMatch>
I put this .htaccess file in the “api” directory on the server. Essentially it says that any file with “api” in it’s name should be interpreted as PHP. This will only work on Apache servers and will vary a bit from server to server. This is what worked for me on Media Temple (gs).
Make the browser see it as JavaScript
Our file, api.js, is now read to be interpreted as PHP on the server, we just need to make sure it’s still seen as JavaScript by the browser, so let’s make sure by putting this right at the top:
<?php
header('Content-Type: text/javascript');
?>
The Desired JavaScript Output
Before we get started with the PHP, let’s take a look at what we ultimately need from the JavaScript. We need a simple function that uses the getElementById method to target our “quote” div and then use the innerHTML property to inject the quote in the proper markup.
function getQuote(id) {
document.getElementById(id).innerHTML = "<p class='qod-quote'>',QUOTE-GOES-HERE,'</p><p class='qod-author'><a href='',PERMALINK-GOES HERE,''>',AUTHOR-GOES-HERE,'</a></p>";
}
Don’t worry about the QUOTE-GOES-HERE business, that is what we’ll need the PHP for later. Then we need to call this function when the host site has loaded:
window.onload = getQuote("quote");
Getting the Data with PHP
Clearly we need to fill in the blanks in the function above with actual data. This is what we need the PHP for. The data resides in a WordPress database, so we need to connect to that database and then fashion a query to get what we need.
Connect to database
$link = mysql_connect('localhost','database_user','database_password');
mysql_select_db('database_name',$link);
Query for a random quote
The following will grab a single random entry from the database. Much of this is specific to a WordPress database, so we do special things like making sure the entry is published and is a post (as opposed to a page).
$query = 'SELECT post_content AS quote, post_title AS author, guid AS permalink FROM wp_posts WHERE post_status = 'publish' AND post_type = 'post' ORDER BY rand() LIMIT 1';
$result = mysql_query($query,$link) or die(mysql_error().': '.$query);
Turn result into an array
$row = mysql_fetch_assoc($result);
Inserting Data into Function
We already saw what we want the final JavaScript to look like, but now we can populate it with the proper data! Most of the data we can drop right in from our $result array, but the quote needs a little special cleaning up to account for special characters:
$quote = str_replace('"','"',$row['quote']);
And the final function, we can simple echo out:
echo 'function getQuote(id) { document.getElementById(id).innerHTML = "<p class='qod-quote'>',$quote,'</p><p class='qod-author'>',$row['author'],'</p>"; } window.onload = getQuote("quote");';
As you can see after we define the function, we call it during the window’s onload event, which triggers it when the page calling it is ready.
Do I have options?
Just one, if you really don’t want the permalink on there linking back to Quotes on Design, you can turn it off by appending a paramater (?link=no) to the URL. Yet another cool reason to use the PHP / JavaScript combo!
<script type="text/javascript" src="http://quotesondesign.com/api/api.js?link=no"></script>
This code should be fairly easy to adapt to add an “API” for almost any WordPress blog.
Need little API Chris! JavaScript is very handy in that it can be generated using PHP and avoid a lot of Client-Side programming.
Forgive me, please if these are dense questions:
Was the aliasing of the js file with api in the name done so it is parsed as PHP to overcome the possible restriction ofnot being able to server-side-include non-same-domain files (as when adjusting url_fopen or similar within PHP.INI to tighten security)?
Also, Why must an onload function be run since the PHP can (as you’ve shown) just directly echo the output (and div wrappers) from anywhere you place the quote in your webpage?
Thanks for your time (& excellent ‘api’), Chris & David!
1. There really was no need for the htaccess trickery … just http://quotesondesign.com/api is enough … you can make /api/index.php server a JS file via content-type header
2. Why are you overloading window.onload .. wouldn’t that override any other window.onload behavior ?
3. What I would rather do instead of a php api is make all the quotes as a text file (say 1.xml , 2.xml …. ) and make the javascript (simple javascript .. no php) choose a random number and then download and show that quote (XMLHttpRequest should do it)
Main point is server doesn’t need to keep running php , do random and then do a db query everytime for each and every request for a quote
e.g api.js would select a random number , download $RANDOM.xml and use it to add behavior to window.onload and put that $RANDOM.xml quote into the page.
you will need to keep modifying api.js as the number of quotes increase , but other than that it should all be fine …
every new quote you would publish to CURRENT_MAX_QUOTES+1.xml
Nice idea! Personally, I prefer using JSON instead of XML, especially considering the simple data structure…
Useful. Thanks for the “Tricky Tricky” section. :-)
I hate php, got used to rails and haven’t looked back at php since.
@duryodhan, your solution sounds nice, but why have individual Xml-sheets? Why not let the quotes be tags like:
<quote>
<quote-content>
"Hello."
</quote-content>
<quote-author>
"John Smith"
</quote-author>
</quote>
I’m not handy with XML, just wondered why multiple files should be used…
I’ve done something similar to this once, to syndicate headlines across a network of blogs. It’s quite a useful trick.
the htaccess magic is really unnecessary since it only beautifies the url of your js file — the browser won’t care for file extensions, setting the content-type is all that you need to do.
So you could as well rename api.js to api.php and tell people to include the following:
<script type="text/javascript" src="http://quotesondesign.com/api/api.php?link=no"></script>
And also, you should establish some kind of caching to prevent having to connect to MySQL just to get a random quote. For example, you could store all your quotes in a file and randomly select a quote from the file instead of from the DB:
Get your quotes in an array and store the array using serialize() + unserialize() **or** (I prefer this one because performance seems to better) use var_export() and include the file as php file
Establish a routine for refreshing the cache (for example if cache file is older than one hour — filemtime() )
Select a random quote using array_rand()
I just tried your suggestion with a simple PHP file that just echoes “hello world” to the browser. Nothing happens even with with allow_url_fopen = on and allow_url_include = on in the php.ini file. First I tried the src=non-same-domain type of php file, and then a local php file that includes the non-same-domain php file. Both resulted in no joy. Any suggestions?
In case you were wondering, with allow_url_fopen = on and allow_url_include = on, if I simply include the local php file (and it in turn includes the non-same-domain php file), as expected, I get the output of “hello world”.
So sorry if I am misunderstanding your problem, but are you attempting his first suggestion, just including javascript with the file extension php? If so, then as far as I know “allow_url_include” and “allow_url_fopen” have nothing to do with this.
Remember that the PHP script must echo the javascript code. If you access the PHP script from your browser and it simply prints “hello world” to the screen, this would be like including a javascript file with the contents, “hello world.” Try having the PHP echo the following: alert(‘hello world’); which can be changed to the javascript code that dynamically puts “hello world” into the page instead of an alert.
Also remember to include the header statement that Chris mentioned above.
Sorry again if I misunderstood what you are trying to do.
Thank you Chris and David for creating this! Great job Chris on the post as always! I remember first discovering that PHP could print javascript code that could be included in a page. I was pretty amazed. . .
Wow, I see some mad science stuff going on here. Thanks Chris. Always a pleasure reading your posts. Keep it mad!
@Paul : Pretty much the same as I was saying
@Johan :
thats just 1 quote.. Chris has a thousand quotes and he wants to show one randomly … thats why I said he can have 1.xml, 2.xml … 1000.xml .. the Javascript will generate a random number and then download the appropriate .xml .. all will have the same stylesheet,
I am a big believer in KISS ergo my choice
and choosing JSON/XML/microFormat whatever is secondary .. my main point was the choice of design …
Yeah, I know, but in the little I’ve read about XML, it is said that you with Javascript can access all nodes in an XML-sheet, like an array: quote[0], quote[1] … quote[1000]. I may be wrong, but it seems to me quite weird to make a new file for every new quote. Don’t you think?
You could also make a PHP API.
api.php
$quote = getQuoteFromDatabase(); // returns array
echo serialize($quote);
site.php
$quoteData = file_get_contents('http://qod/api.php');
$quote = unserialize($quoteData);
echo $quote['author'] . ' said: ' . $quote['quote'];
Can you add a far future expires header for http://quotesondesign.com/api/api.js? This will make YSlow a little happier.
Hey i guess we could use a Download file, not we? :D