PHP: How to easily provide JSON and JSONP

Would you like to grab some server-side data through an AJAX call? For example by using the handy jQuery.ajax method?

A good data format to use then is JavaScript Object Notation, more commonly known as JSON. Providing data in the JSON format with PHP is super duper simple 8)

JSON

All you need to do on the server side is to set the content-type to application/json, encode your data using the json_encode function and output it.

<?php header('content-type: application/json; charset=utf-8');

$data = array(1, 2, 3, 4, 5, 6, 7, 8, 9);

echo json_encode($data);

You can test it out for example in the FireBug console by running this line:

$.ajax({url: 'data.php'})
// Response: [1,2,3,4,5,6,7,8,9]

You should see the request being done in the console and also in the Net tab. If it didn’t work, you might be subject to the following:

Due to browser security restrictions, most “Ajax” requests are subject to the same origin policy; the request can not successfully retrieve data from a different domain, subdomain, or protocol.

A nice and simple solution to that problem is JSON with padding, also known as JSONP.

JSONP

As stated on Wikipedia,

JSONP or “JSON with padding” is a complement to the base JSON data format, a usage pattern that allows a page to request and more meaningfully use JSON from a server other than the primary server.

I’m not sure I get everything about JSONP, but I have used it and I know that it works :P It’s almost just as easy as plain JSON actually, and all that’s needed is to wrap the JSON encoded data in a callback function provided as a GET parameter.

<?php header('content-type: application/json; charset=utf-8');

$data = array(1, 2, 3, 4, 5, 6, 7, 8, 9);

echo $_GET['callback'] . '('.json_encode($data).')';

You can test it by running

$.ajax({url: 'data.php', dataType:'jsonp'})
// Response: jsonp1277656587731([1,2,3,4,5,6,7,8,9])

This time you won’t see the call in the FireBug console though, but only in the Net tab. That’s because the data is loaded in a script tag instead of through an actual AJAX call. Thankfully jQuery handles all of that for you behind the scenes, so you don’t have to worry about it at all. You’ll get the same JSON data in your response handler, ready to be used however you want to :)

What if you’d like to provide both formats?

JSON and JSONP together

Easy as cake actually. You just need to check if the callback parameter is set or not:

<?php header('content-type: application/json; charset=utf-8');

$data = array(1, 2, 3, 4, 5, 6, 7, 8, 9);
$json = json_encode($data);

echo isset($_GET['callback'])
    ? "{$_GET['callback']}($json)"
    : $json;

And that’s all! Although there are some additions you could do if you want:

Cross-Origin Resource Sharing (CORS)

When testing this you might have noticed it will not work cross-domain. This is because of security stuff in your browser. So, if you want everyone to be able to get access to some JSON data of yours, you need to flag that this is OK. One way to do this, is through CORS:

Cross-Origin Resource Sharing (CORS) is a specification that enables a truly open access across domain-boundaries.

CORS defines how browsers and servers communicate when accessing sources across origins using HTTP headers to allow both the browser and the server to know enough about each other to determine if the request or response should succeed or fail. — enable-cors.org

You can read more about it on the enable-cors.org website, but all you should need to do if you want your JSON data accessible from everywhere is to add a single header.

<?php
header('content-type: application/json; charset=utf-8');
header("access-control-allow-origin: *");

// Rest of script...

It should now be accessible from everywhere :)

Added security

Since the sender can send whatever they want as a callback, you could potentially have someone injecting malicious code there. I’m not sure how likely this is to happen, but it is a possibility. Could for example be used to steal cookies. Not the baked kind… So, although it does make things a bit uglier, it’s better to be safe than sorry I suppose? So I did some research and ended up with a function I could use to check that the callback is indeed a valid JavaScript identifier. Here’s the code with the added security.

<?php header('content-type: application/json; charset=utf-8');

function is_valid_callback($subject)
{
    $identifier_syntax
      = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*+$/u';

    $reserved_words = array('break', 'do', 'instanceof', 'typeof', 'case',
      'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue',
      'for', 'switch', 'while', 'debugger', 'function', 'this', 'with',
      'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum',
      'extends', 'super', 'const', 'export', 'import', 'implements', 'let',
      'private', 'public', 'yield', 'interface', 'package', 'protected',
      'static', 'null', 'true', 'false');

    return preg_match($identifier_syntax, $subject)
        && ! in_array(mb_strtolower($subject, 'UTF-8'), $reserved_words);
}

$data = array(1, 2, 3, 4, 5, 6, 7, 8, 9);
$json = json_encode($data);

# JSON if no callback
if( ! isset($_GET['callback']))
    exit($json);

# JSONP if valid callback
if(is_valid_callback($_GET['callback']))
    exit("{$_GET['callback']}($json)");

# Otherwise, bad request
header('status: 400 Bad Request', true, 400);

Please comment if you have any feedback :)

Update: Added charset to the content-type header as recommended in an answer to a question I posted about this on StackOverflow.

Update: Added stuff about callback checking. Recommended in new answers to my question at StackOverflow.

Update: Discovered an easy way to compress the JSON data! Blogged about it too :)

Update: Added some info about cross-origin resource sharing

21 thoughts on “PHP: How to easily provide JSON and JSONP

  1. Hi Torleif,

    I have followed your instruction on jsonp. May firebug console still show the ajax request. May I know is there any more step which I have missed?

    Thank you
    Benjamin.

    • I’ve messed around a bit, and although I’m not 100% sure why this happens, it seems to happen when loading a resource from the same domain. In this case JSONP isn’t actually needed either, but I have no clue if there is a connection there.

      When I did $.ajax({url: '/data.php', dataType:'jsonp'}), it showed up in the console. But if I did $.ajax({url: 'http://ajax.googleapis.com/ajax/services/search/blogs?v=1.0&q=geekality', dataType:'jsonp'}), then it didn’t. However, in both cases the request does show up in the Net tab. And if you watch closely in the HTML tab, you will see the head tag highlight for a bit when the script tag is added and removed to load the data.

    • Asked about case-sensitivity of the HTTP headers on StackOverflow, and according to the HTTP RFC the headers are case-insensitive. So that shouldn’t be an issue :)

      What do you mean by semi-colon at the end of the response? The end of the header string, or at the end of the JSON output? And should it be semi-colon both for JSON and JSONP, or just one of them? Haven’t seen a semi-colon at the end of any HTTP header, JSON or JSONP examples before…

  2. Pingback: jsonp.. simple way of doing it with php | Hi, I'm Mat

  3. Great post!

    I’ve found your blog at Google search when trying to implement the JSONP format and it fitted like a glove. =)

  4. Hey, great post save me a lot of time searching the web….
    i have a question…
    i want the response of this jsonp call be surrounded by
    {

    }
    instead im gettin
    [
    {
    }
    ]
    can anyone tell me or helping me on this?

    thanks

    • Make sure you’re encoding what you want, and try to adjust the output of json_encode by using one of the options. Think [ ] is for arrays. Are you encoding an array or an object? Maybe the JSON_FORCE_OBJECT option could help?

  5. Hi all Sencha Touch Dev,
    i am a new Dev that i have one problem need someone to help something,I want to get Json Data Transfer from url:http://townhub.bmsolution.biz/mysql2json-2010-11-07/example.php but it error. SMS Error on Fire buge: invalid label
    [Break On This Error] “data”: [
    here is my code:

    // JavaScript Document

    Ext.application({
    name: 'Sencha',
    launch: function() {
    Ext.create('Ext.DataView', {
    fullscreen: true,
    store: {
    autoLoad: true,
    //fields: ['from_user', 'text', 'profile_image_url'],
    fields: ["id","cust_firstname", "cust_lastname","cust_email"],

    proxy: {
    type: ‘jsonp’,
    url: ‘http://townhub.bmsolution.biz/mysql2json-2010-11-07/example.php’,

    reader: {
    type: ‘json’,
    root: ‘data’
    }
    }
    },
    /*
    itemConfig: {
    tpl: ‘{from_user}{text}’
    }*/

    });

    }
    });

    pls help me, and thank u for advance… for reply me i allway waitting .,

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>