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 2010-12-11: Added charset to the content-type header as recommended in an answer to a question I posted about this on StackOverflow.

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

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

Update 2012-01-08: Added some info about cross-origin resource sharing

  • Benjamin

    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.

    • http://www.geekality.net Torleif

      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.

  • http://changedmy.name Xiong Chiamiov

    “Content-type” needs to be capitalized. Also, there should be a semi-colon at the end of the response.

    • http://www.geekality.net Torleif

      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…

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

  • jaime

    Hi,
    im getting some bad times with those cross domains limitations.
    anyhow, i found your page, and it was cool (even if its quite old, maybe add another link could also contribute).

    http://stackoverflow.com/questions/1678214/javascript-how-do-i-create-jsonp

    Best regards,

    • http://www.geekality.net Torleif

      Quite old? I updated it last month. Is something incorrect, or missing? Why would adding links contribute to? Links to what?

  • http://www.fighne.com peter feeney

    The missing piece is this
    header(‘Access-Control-Allow-Origin: *’);
    so this could not have been tested crossdomain, more likely same domain testing!
    regards pete

    • http://www.geekality.net Torleif

      Cool, didn’t know about that. Will have to look into that a bit :)

    • http://www.geekality.net Torleif

      Added a bit about this now. Again, thanks for the heads up!

  • http://jediscode.blogspot.com/ Jed Hunsaker

    Great post! I don’t know what these other guys are complaining about. This really cleared things up for me and helped me solve my JSON(P) woes.

    • http://jediscode.blogspot.com/ Jed Hunsaker

      Oh, but one thing I do in my json.php is disable caching, like so:

      header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
      header("Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . "GMT");
      header("Cache-Control: no-cache, must-revalidate");
      header("Pragma: no-cache");
      • http://www.geekality.net Torleif

        Is that necessary though? Might be nice some times I guess.

  • Rafael

    Great post!

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

    • http://www.geekality.net Torleif

      That’s terrific! :D

  • http://hypem.com Anthony Volodkin

    Very handy and simply explained, thank you!

  • http://richstokoe.com Rich Stokoe

    Great post! Thanks for your hard work putting this together.

  • Juan

    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

    • http://www.geekality.net Torleif

      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?

  • http://bmsolution.biz Bouheang

    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 .,

    • http://www.geekality.net Torleif

      When visiting that page I got the JSON fine. Sure it supports JSONP? Don’t know any Ext, so can’t help you much there.

  • sam

    Thanks for this helped me out loads ;)

    • http://www.geekality.net Torleif

      Great to hear!

  • Clare

    Getting the code right..!!!!

    Hi,
    I have a ‘callback’ script that gets called directly from the server, the script gets run and the below creation of the text file gets executed successfully, that is however until I add the javascript code below it, then it stops working…. can
    someone tell me if I’m barking up the wrong tree here ?

    All I’m trying to do here is send data to my web service, I don’t need anything returned back from…

       // mycallback.php

       $.ajax({
         dataType: 'jsonp',    
         type: 'POST',
         data: { strFirst: JSON.stringify("FIRST")},
         jsonp: 'jsonp_callback',
    url: 'http://pretendserver.co.uk/WebService1/service1.asmx/HelloWorld',    
         contentType: 'application/json;
         charset=utf-8'

    });
  • Clare

    getting the code right..contd..

    As above, looks like my php code got removed from the comments box when it posted..oh dear, let me try again.. I’ll remove the usual php script tags and just say PHPST instead…

    // mycallback.php
    <?php header("content-type: application/json; charset=utf-8");
    session_start();
    require_once("json2.js");
    require_once("jquery-1.7.1.js");
    session_destroy();
    $Handle = fopen("TEST/".date("YmdHis").".".$ext, 'w');
    $Data = "Test";
    fwrite($Handle, $Data);
    fclose($Handle);
    ?>
       $.ajax({
         dataType: 'jsonp',    
         type: 'POST',
         data: { strFirst: JSON.stringify("FIRST")},
         jsonp: 'jsonp_callback',     url: 'http://pretendserver.co.uk/WebService1/service1.asmx/HelloWorld',
         contentType: 'application/json; charset=utf-8'  
    });
    • http://www.geekality.net Torleif

      Tried fixing the code for you (you can just wrap it in code tags). Anyways, not quite sure what you’re trying to do. Also, if it’s an issue which is more about general javascript or php, I’d recommend StackOverflow :)

  • Gus
    //ORIGIN
    *****************************************************
    //Destination NAME: jsonRead.php

         $("document").ready(function(){
            $.ajax({type: "GET",
                url: 'jsonData.php',
                dataType: "jsonp",
                  crossDomain: true,
                beforeSend: function(x) {
                    if(x &amp;&amp; x.overrideMimeType) {
                    x.overrideMimeType("application/j-son;charset=UTF-8");
                    }
                },
                  success: function(data){
                         if(data){
                             $("#jsonInfo").html(data);
                         }
                         else{
                             $("#jasonInfo").html("Your data is empty");
                         }
                     }
            });
               
        });

    When I browse do not return the data, if I remove in the ajax dataType: “jsonp” it is working, I do not understand the callback, do I have to do something like …. please help?

    • http://www.geekality.net Torleif

      Why in the world are you doing the overrideMimeType stuff? Otherwise I don’t know what could be wrong. jQuery should handle the jsonp and callback parameter automatically when you set the dataType to jsonp. Maybe check out the jQuery documentation to see if you’re using it wrong. (You’ve also spelled the element id wrong in your else statement. You should learn to check your spelling better. Typing correctly is important when you write code :)

      • Gus

        Please help me to find the problem, I removed the overrideMimeType and still not receiving the jsonp, please help me about the callback how it works? should I or or or
        ???? sorry I do not undestand the callback.

        • http://www.geekality.net Torleif

          If you use jQuery correctly, you shouldn’t have to do anything about the callback. It should handle it for you. Other than that I’m afraid I don’t know what’s going wrong for you. Maybe try StackOverflow instead.

          • Gus

            ok, please try it, take your php “Added security” code from this page, and the code I posted jsonRead.php, both local host, and try it to work, if you remove dataType: “jsonp”, is working, if you remove “beforeSend: function” stop working.
            Please send me a sample working by email, have been 5 days and I cant understand what is going on. thank you.

          • http://www.geekality.net Torleif

            I’m sorry, but I don’t have time or energy to debug the code of other people :) Please try StackOverflow or CodeReview if you need help.

    • Toby1

      There is a typo in your success function #jasoninfo” instead of #jsoninfo. Also, should you not include jsonpCallback: “callback”, as one of parameters after datatype? This works for me:

      function getcode(){
          $.ajax({
              url: 'http://myserver.com/myphp.php',
              dataType: "jsonp",
              jsonpCallback: "_testcb",
              cache: false,
              timeout: 5000,
              success: function(data) {
                  alert(data);
              },
              error: function(jqXHR, textStatus, errorThrown) {
                  alert('error ' + textStatus + " " + errorThrown);
              }
          });
      }
      • http://www.geekality.net Torleif

        Correct about the typo! But I think jQuery is supposed to add a random callback name on its own. I have only set it myself one time when the API required it to be equal to a certain string.

  • Gus

    Oooo,, then close the post,, if you don’t have time to help.

    • http://www.geekality.net Torleif

      No, cause I’d still like to know if anyone for example find errors in my code.

  • Toby1

    Great and helpful stuff. Exactly what I was looking for. Thanks man.

  • Mike

    Thanks, great article. Didn’t know about the Access-Control-Allow-Origin: * header so that was handy.

    For the JSONP output (when there’s a callback) I think it’s good practice to have a semi-colon on the end of it e.g. callbackname({“name”:”thedata”}); as it is actually JavaScript and will be inserted into script tags on the client end. Semi colons are optional in JavaScript though so it probably won’t error. For the plain JSON (without a callback), you’d leave it off and just output the plain json… then you can use a Firefox addon like JSONovich to display the output nicely in the browser (great for testing).

  • Pingback: jQuery onsuccess not working even though firebug showing the response ok | Easy jQuery | Free Popular Tips Tricks Plugins API Javascript and Themes()

  • http://wodtracker.net acedanger

    Excellent writeup. Exactly what I needed for moving my APIs to their own subdomain and then accessing them from multiple places.

    • http://www.geekality.net Torleif

      Great to hear it worked! :)

  • Tim

    Thanks! Concise and very to-the-point. The tips worked like a charm.

  • NeoFuture

    You sir are a gentleman for sharing your resource on this, fixed my issue straight up, i dont normally comment and say thank you, but you deserve it :D awesome post !!!!

  • Stawi

    I dont get checking of valid callback..
    Who can call $.ajax(..) on site? I think only site owner (you need to put it in js/html files), and I doubt if one want to hack own site with invalid callback? or am I wrong?

    BUT, if using remote JSONP the answer can be danger!
    Ie. if I put on my site script with src=”http://server2.example.com/RetrieveUser?UserId=1234&jsonp=parseResponse” the server2.example.com can can return / execute ANY script on my site – dangerous! Or am I wrong again? ;)

    • http://www.geekality.net Torleif

      Well, I’d probably just skip it. But remember that HTML and JavaScript is on the client side, and can be edited without much problem.

    • http://twitter.com/LoneIgadzra LoneIgadzra

      If your site is vulnerable to XSS, an insecure JSON-P api can be easily used to compromise a user’s browser. But a hacker can just use their own.

  • Demas

    you save my world..

  • Kyle Behse

    Dude, you’re awesome!!

  • http://twitter.com/hybrisCole Alberto Cole

    Thanks dude :>

  • Pingback: Php script installation service()

  • gcolaiac

    Great post ! Thank you for the clarity and completeness,
    I have spent hours and hours trying and reading dozens of posts, including several stackoverflow ones, without success.
    I did susseed with your explanations. Thank you :-)
    (I am bookmarking your link now :-) )

  • passerby

    This is just too awesome! thanks!

    • http://www.geekality.net/ Torleif Berger

      Hopefully not too awesome to make use of though! If so let me know so I can dial it down a few steps :P

  • duplich

    for real, this is awesome, i also have been spending hours, and you pointed it all out in a short and simple post :)

    • http://www.geekality.net/ Torleif Berger

      That’s what I’m going for, so good to hear it worked! :D

  • Stephen Ngethe

    Wish I had seen this four hours ago I could have save lost of time… Big Ups it simple and to the point THANK YOU A MILLION

  • Mramagem

    Great Work! Best and simplest example I’ve found :-)

  • Manuel Trinidad Garcia

    Thanks!!!!!

  • JohnAH

    Thanks so much. I suppose it’s taken me a couple of days trying to sort this out (as mentioned by some others) and your article solved it for me too. (The header statements were what I needed!)

  • Kem Muhammad

    Thanks! All the answers in one place.

  • mrgingrich

    Very, very helpful. Thanks!

  • cykan

    Thank you very much! :)

  • Pingback: Ferien / Feiertag API für Deutschland - SmartNOOB Blog()

  • Matteo Candura

    You saved my life, thanks!

  • abel

    Finally what I was looking for !! Very clear very simple THANK YOU !!!

  • lye

    Thank you! You are awesome.

  • Pingback: Ferien / Feiertag API für Deutschland | SmartNOOB Blog()