0

Question

How can i implement a PHP callback being passed over AJAX with the PHP callback being called by the page requested by AJAX?

The Setup

Comments are posted via AJAX and the paramaters are passed serialized and encrypted (so they can't be changed in transit or carefully crafted AJAX requests to abuse the comments system). The problem is, i need the new total comments amount to update a field in a different mysql table (which will change in every place the comments are used) than what the comments are in themselves.

Example

Someone leaves a comment on a forum topic, that topic needs to know the total comments (without querying the comments table everytime this needs to be known). The problem is, when the comments are posted via AJAX we don't know what the table is to update, what fields, etc as well as having to execute additional code along with it (such as also logging all the members who have left comments on the specific topic).

Solution

I thought about adding two PHP callbacks using closures when declaring the comments widget. These two callbacks (onSuccess() onFailure()) would then be able to do what ever work is needed such as counting the total comments and updating the total comments count for the specific forum topic. Then serialize, encrypt it, pass it as a parameter over ajax, then PHP to decrypt and unserialize the callbacks and execute them.

Why The Solution Breaks

Because closures can't be serialized! Also, i am NOT using eval before anyone suggests it.

The Question Again

How can i implement a PHP callback being passed over AJAX with the PHP callback being called by the page requested by AJAX?

Update

It would appear that some of you are not reading the whole question and understanding that callbacks are to be done in PHP - NOT javascript (using the AJAX callbacks). AJAX is only used to transport the posted comment - not process it (that's what PHP does and that's where the callback comes in which is the problem).

HenchHacker
  • 1,616
  • 1
  • 10
  • 16

4 Answers4

1

Create a function that is the callback, then pass the function name via ajax, and call it like this:

$callback = 'callback_function_name'; $callback($params...);
HenchHacker
  • 1,616
  • 1
  • 10
  • 16
  • Just thought of this way after posting the question, but not sure if it's ideal... will leave the voting to you guys based on what other solutions come up. – HenchHacker Oct 31 '12 at 13:42
  • That looks like a reasonable solution. I am thinking of other options. Will reply if any alarm bells ring. – Abhishek Saha Oct 31 '12 at 13:44
  • 2
    This is fine. Just use a whitelist or some other highly effective validation scheme. – goat Oct 31 '12 at 13:52
0

Maybe I don't get what you're trying to do, but could you do something like:

window.myappstuff={}

window.myappstuff.onSuccess=function(){
   //Do some stuff here.
}

window.myappstuff.onFailure=function(){
   //Do some different stuff here.
}

Your php callback could then call window.myappstuff.onSuccess or window.myappstuff.onFailure. You could also use random function names instead.

Joshua Dwire
  • 5,415
  • 5
  • 29
  • 50
  • Sorry, the callbacks need to be done in PHP and NOT JavaScript. That's where the problem is, how to transport the PHP callbacks over AJAX. – HenchHacker Oct 31 '12 at 13:45
  • So the problem is with javascript calling php with the result, not with php calling javascript with the result? – Joshua Dwire Oct 31 '12 at 13:47
  • JavaScript is just a transport in this case. I don't care about the JavaScript success or failure (thats a different story). See my possible answer, might help you understand. – HenchHacker Oct 31 '12 at 13:48
  • When you send a request via ajax, why dont have a action variable ? action may be update,insert etc..and also the fieldnames. – Abhishek Saha Oct 31 '12 at 13:51
  • Because of this "as well as having to execute additional code along with it (such as also logging all the members who have left comments on the specific topic)." which is where a callback becomes in. – HenchHacker Oct 31 '12 at 13:59
0

On the whole, I'd have to recommend a rethink. It's considered bad practice to send anything even remotely code-like via ajax, for security reasons. I could, for example, "intercept" the reply using firebug or something, change whatever to my code.
If you send a function name and arguments, I can change them. Suppose your script validates user input, I could just make it so that an ajax call returns a non-existing function. Your script will fail, and with it all form of input validation is gone.
If you send a full function definition as string, I can go even further.

You might have seen people do something like this:

echo 'function(argument){ console.log(argument);}("something"));';

This is just plain text, no JSON. Then, in JS they'll do something that should be considered a crime against humanity:

//readystatechange callback:
if (this.readyState === 4 && this.status === 200)
{
    return eval(this.responseText);//<=== EVIL - EVIL - EVIL
}

This is insane it's EVIL don't even look at this
What you can do as some sort of quick fix is write a generic onreadystatechange function:

//readystatechange
if (this.readyState === 4 && this.status === 200)
{
    var returnObj = JSON.parse(this.responseText);
    switch(returnObj.actionType)
    {
        case 'DomAction':
            return domSwitch(returnObject.data);//<-- based on data domSwitch will decide what to do
        case 'setVar':
            return varSwitch(returnObjcet.data);
        default:
            return educatedGuess(returnObject);//<-- desperate
    }
}

But again, when you send a request, you generally have a certain expectation as to what kind of data you'll get back, and what you want to do with it, so you could create a readystatechange handler on the fly for each ajax request, to deal with that particular call.

Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • "I could, for example, "intercept" the reply using firebug or something, change whatever to my code." - no you cant, as i mentioned the params are serialized and encrypted (512 bit) and that key changes every 24 hours. To prevent just that. – HenchHacker Oct 31 '12 at 14:01
  • Also, the solution needs to be done in PHP - not javascript. I know everyone instantly assumes callbacks relating to javascript - but you can implement them in PHP using closures in PHP ;) – HenchHacker Oct 31 '12 at 14:02
  • @VBAssassin: Sorry, missed that part, also: I was thinking you were working the other way around (PHP -> JS, not JS -> PHP function). Still: encrypted data can still be _corrupted_, in which case you're unable to decrypt the data – Elias Van Ootegem Oct 31 '12 at 14:06
  • Yeah, which is why it returns false if it can't be decrypted (since it's changed) and returns false if it doesn't deserialize and also returns false if some of the posted values don't exist. 3 levels of checks. Also, with the key changing every 24 hours i can't see brute forcing working either. As an extra security measure i could hash with a private key which would verify the integrity of the encrypted data (without others being able to produce the hashes) - but that's a little extreme i think > – HenchHacker Oct 31 '12 at 14:09
0

Instead of passing callbacks, create the functions in advance on the server and pass the name of the function to execute, with list of arguments to pass it.


Besides, here is the place where you start using angular.js. This way you'll have a client side model of the forum topic with a field of posts_count which you'll update on on the client side and angluar's bindings will update the view wherever you use data from this model.

Alex
  • 11,479
  • 6
  • 28
  • 50