-1

There's a lot of code in each file, too much to post, so I'm giving you a general idea of what's happening in each file.

index.php [html dropdown menu code etc.]

scripts.js [AJAX detects user selection from dropdown, grabs fetch.php which pulls database to generate html code for secondary dropdown selections to put in index.php]

fetch.php [Generates secondary dropdown code based on user selection and query of the database]

I need to see what exactly is being queried to debug, so I'd like to echo the sql select statement:

$query = "SELECT * FROM databasename WHERE.."

That is in fetch.php when user makes a selection from index.php - How do I do this?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
isosmall
  • 21
  • 1
  • 8
  • "There's a lot of code in each file, too much to post..." sounds like what happens when you just have at it with PHP instead of having a strategy for organizing your application into well-defined concerns. These ad-hoc solutions are often thin on conventions which means we can't really speak to your application, it could take any of an infinite number of forms. – tadman Aug 09 '18 at 15:41
  • If you're just getting started with PHP and want to build applications, I'd also strongly recommend looking at various [development frameworks](http://codegeekz.com/best-php-frameworks-for-developers/) to see if you can find one that fits your style and needs. They come in various flavors from lightweight like [Fat-Free Framework](https://fatfreeframework.com/) to far more comprehensive like [Laravel](http://laravel.com/). These give you concrete examples to work from and much stronger guidance on how to write your code and organize your files. – tadman Aug 09 '18 at 15:42
  • 1
    If this is in production then **DO NOT ECHO**. Write the debug information which you need to a log file and check the log file after performing your AJAX action. – MonkeyZeus Aug 09 '18 at 15:43
  • Echo the contents and do a die() or exit; afterwards... then in the Network tab of your browser, start it recording, run the Ajax request (it'll fail) but check the resource/name and then view the Response, and it'll show you what was echo'd in the script – Adam Aug 09 '18 at 15:43
  • But obviously if it's in production listen to @MonkeyZeus – Adam Aug 09 '18 at 15:44
  • 1
    On top of what @tadman said, if you're looking for a lightweight PHP framework that is modular in nature and uses an MVC structure, you could check out my fork of Fat-Free-Framework that I call [Grump-Free-Framwork](https://github.com/GrumpyCrouton/grump-free-framework) – GrumpyCrouton Aug 09 '18 at 15:45
  • 1
    @MonkeyZeus Debugging on production is also a sign that a development environment is urgently needed. Setting up an XAMPP or WAMP or Vagrant-driven build environment isn't all that hard, and is essential for safe and efficient development. – tadman Aug 09 '18 at 15:47
  • @tadman In general, yes I agree. However, It depends. Sometimes bugs are only catch-able trace-able in a production environment due to either environment or data discrepancies. If the code base is truly that large then I would imagine the dataset could be equally or even more large/complex. Based on the composition of the question, what's easy for you is not as easy for OP so if there are time constraints then spending a week learning about server administration might not be practical until after the current bug is resolved. – MonkeyZeus Aug 09 '18 at 16:20
  • @MonkeyZeus Debugging on production is something you can hopefully avoid entirely. Hot-patching production code is always extremely risky. – tadman Aug 09 '18 at 16:22

2 Answers2

5

When I deal with AJAX, that I return as JSON, one trick I use is to take advantage of output buffering. You can't just echo or output anything you want because it will mess up the JSON data so for an example,

ob_start(); //turn on buffering at beginning of script.

.... other code ...

print_r($somevar);

.... other code ...

$debug = ob_get_clean();  //put output in a var
$data['debug'] = $debug;

header('Content-Type: application/json');
echo json_encode($data); //echo JSON data.

What this does, is wrap any output from you script into your JSON data so that it's format is not messed up.

Then on the javascript side you can use console.log

$.post(url, input, function(data){
   if(data.debug) console.log(data.debug);
});

If you are not used to debugging with console.log(), you can usually hit F12 and open the debugger in most browsers. Then in there the output will be sent to the "console". IE9 had a bit of an issue with console.log() if I recall, but I don't want to go to far off track.

NOTE: Just make sure to not leave this stuff in the code when you move it to production, its very simple to just comment this line out,

//$data['debug'] = $debug;

And then your debug information wont be exposed in production. There are other ways to automatically do this, but it depends on if you do development local then publish to the server. For example you can switch it on the $_SERVER['SERVER_ADDR']; which will be ::1 or 127.0.0.1 when it's local. This has a few drawbacks, mainly the server address is not available from the Command Line Interface (CLI). So typically I will tie it into a global constant that says what "mode" the site is in (included in the common entry point, typically index.php).

if(!defined('ENV_DEVELOPMENT')) define('ENV_DEVELOPMENT','DEVELOPMENT');

if(!defined('ENV_PRODUCTION')) define('ENV_PRODUCTION','PRODUCTION');

if(!defined('ENVIRONMENT')) define('ENVIRONMENT',ENV_DEVELOPMENT);
//site is in Development mode, uncomment for production
//if(!defined('ENVIRONMENT')) define('ENVIRONMENT',ENV_DEVELOPMENT);

Then it is a simple matter to check it:

if(ENVIRONMENT == ENV_PRODUCTION ) $data['debug'] = $debug;

If you know how to use error reporting you can even tie into that using

 if(ini_get('display_errors') == 1) $data['debug'] = $debug;

Which will only show the debug when display errors is on.

Hope that helps.

UPDATE

Because I mentioned it in the comments, here is an example of it wrapped in a class (this is a simplified version, so I didn't test this)

class LibAjax{
    public static function respond($callback, $options=0, $depth=32){
        $result = ['userdata' => [
              'debug' => false,
              'error' => false
        ]];

        ob_start();

         try{

             if(!is_callable($callback)){
                //I have better exception in mine, this is just more portable
                throw new Exception('Callback is not callable');
             }

             $callback($result);
         }catch(\Exception $e){
              //example 'Exception[code:401]'
             $result['userdata']['error'] = get_class($e).'[code:'.$e->getCode().']';
            //if(ENVIRONMENT == ENV_DEVELOPMENT){
            //prevents leaking data in production
                $result['userdata']['error'] .= ' '.$e->getMessage();
                $result['userdata']['error'] .= PHP_EOL.$e->getTraceAsString();
            //}
         }

         $debug = '';
         for($i=0; $i < ob_get_level(); $i++){
             //clear any nested output buffers
             $debug .= ob_get_clean();
         }
         //if(ENVIRONMENT == ENV_DEVELPMENT){
             //prevents leaking data in production
              $result['userdata']['debug'] = $debug;
        //}
         header('Content-Type: application/json');
         echo self::jsonEncode($result, $options, $depth);
   }

   public static function jsonEncode($result, $options=0, $depth=32){
       $json = json_encode($result, $options, $depth);
       if(JSON_ERROR_NONE !== json_last_error()){
           //debug is not passed in this case, because you cannot be sure that, that was not what caused the error.  Such as non-valid UTF-8 in the debug string, depth limit, etc...
           $json = json_encode(['userdata' => [
              'debug' => false,
              'error' => json_last_error_msg()
           ]],$options);
       }
       return $json;
   }

}

Then when you make a AJAX response you just wrap it like this (note $result is pass by reference, this way we don't have to do return, and in the case of an exception we update $result in "real time" instead of on completion)

LibAjax::respond( function(&$result){
     $result['data'] = 'foo';
});

If you need to pass additional data into the closure don't forget you can use the use statement, like this.

$otherdata = 'bar';

LibAjax::respond( function(&$result) use($otherdata){
     $result['data'][] = 'foo';
     $result['data'][] = $otherdata;
});

Sandbox

This handles catching any output and puts it in debug, if the environment is correct (commented out). Please pleas make sure to implement some kind of protection so that the output is not sent to clients on production, I cant stress that enough. It also catches any exceptions puts it in error. And it also handles the header and encoding.

One big benefit to this is consistent structure to your JSON, you will know (on the client side) that if if(data.userdata.error) then you have an exception on the back end. It gives you one place to tweak your headers, JSON encoding etc...

One note in PHP7 you'll have to or should add the Throwable interface (instead of Exception). If you want to catch Error and Exception classes Or do two catch blocks.

Let's just say I do a lot of AJAX and got sick of re-writing this all the time, my actual class is more extensive then this, but that's the gist of it.

Cheers.

UPDATE1

One thing I had to do for things to display was to parse the data variable before I console.log() it

This is typically because you are not passing the correct header back to the browser. If you send (just before calling json_encode)

header('Content-Type: application/json');

This just lets the browser know what type of data it is getting back. One thing most people forget is that on the web all responses are done in text. Even images or file download and web pages. It's all just text, what makes that text into something special is the Content-Type that the browser thinks it is.

One thing to note about header is you cannot output anything before sending the headers. However this plays well with the code I posted because that code will capture all the output and send it after the header is sent.

I updated the original code to have the header, I had it in the more complex class one I posted later. But if you add that in it should get rid of the need to manually parse the JSON.

One last thing I should mention I do is check if I got JSON back or text, you could still get text in the event that some error occurs before the output buffering is started.

There are 2 ways to do this.

If Data is a string that needs to be parsed

$.post(url, {}, function(data){
    if( typeof data == 'string'){
        try{
            data = $.parseJSON(data);
        }catch(err){
            data = {userdata : {error : data}};
        }
    }
    if(data.userdata){
          if( data.userdata.error){
               //...etc.
          }
    }
    //....
}

Or if you have the header and its always JSON, then its a bit simpler

$.post(url, {}, function(data){
    if( typeof data == 'string'){
        data = {userdata : {error : data}};
    }
    if(data.userdata){
          if( data.userdata.error){
               //...etc.
          }
    }
    //....
}

Hope that helps!

UPDATE2

Because this topic comes up a lot, I put a modified version of the above code on my GitHub you can find it here.

https://github.com/ArtisticPhoenix/MISC/blob/master/AjaxWrapper/AjaxWrapper.php

ArtisticPhoenix
  • 21,464
  • 2
  • 24
  • 38
  • 1
    I may have to start using this in my projects! – GrumpyCrouton Aug 09 '18 at 15:46
  • In mine I actually use a class that takes a callback closure. Then it expects the results to be in an array $result. So its like `LibAjax::ajaxResponse(function($result){ return $result; })` what this does is besides the output buffering. I have the callback wrapped in a `try/catch` so that I can capture exceptions and put them in an error element all automatically. – ArtisticPhoenix Aug 09 '18 at 15:49
  • I can post an example if you want – ArtisticPhoenix Aug 09 '18 at 15:51
  • That's a great idea, I don't know why I never thought of it. If you want to post an example that would be awesome – GrumpyCrouton Aug 09 '18 at 15:51
  • Thank you for your solution! I'll have to try this. – isosmall Aug 09 '18 at 15:55
  • This is fabulously simple and I love it. – MonkeyZeus Aug 09 '18 at 16:21
  • Thanks, glad you like it. Eventually I will put it with the rest of my stuff on GIT and Composer. – ArtisticPhoenix Aug 09 '18 at 16:23
  • Assuming that JSON output is not guaranteed then you would want something more like: `$debug = ob_get_clean(); $try_json = json_decode($debug); if(json_last_error() === JSON_ERROR_NONE){ /* Do JSON debug stuff */ }else{ /* Not JSON!!! do other debug stuff. Maybe use an HTML comment? */ }` – MonkeyZeus Aug 09 '18 at 16:29
  • Yes, and I do have more extensive checking in my class, but that involves a shared exception class and some other things that are not so portable. In any case I did add some basic checking on it, and a way to access the options of `json_encode` (something i don't have in mine) so I got the defaults for that off of php.net. – ArtisticPhoenix Aug 09 '18 at 18:08
  • I used this and it works! Thanks so much! One thing I had to do for things to display was to parse the data variable before I console.log() it. ... var pData = $.parseJSON(data); console.log(pData.debug); – isosmall Aug 13 '18 at 14:30
  • Typically that happens if you don't include the proper content header, `header('Content-Type: application/json');` But some older browsers have issues with it sometimes, so I always check on the JS side – ArtisticPhoenix Aug 13 '18 at 17:21
  • Well, I learned another thing, thank you! The header did eliminate the need to parse the data in my script file. – isosmall Aug 19 '18 at 12:41
0

Echo the contents and do a die() or exit; afterwards... then in the Network tab of your browser, start it recording, run the Ajax request (it'll fail) but check the resource/name and then view the Response, and it'll show you what was echo'd in the script

Taken from: Request Monitoring in Chrome

Chrome currently has a solution built in.

Use CTRL+SHIFT+I (or navigate to Current Page Control > Developer > Developer Tools.

In the newer versions of Chrome, click the Wrench icon > Tools > Developer Tools.) to enable the Developer Tools.

From within the developer tools click on the Network button. If it isn't already, enable it for the session or always.

Click the "XHR" sub-button.

Initiate an AJAX call.

You will see items begin to show up in the left column under "Resources".

Click the resource and there are 2 tabs showing the headers and return content.


Other browsers also have a Network tab, but you will need to use what I commented to get the string value of the query.

ArtisticPhoenix solution above is delightful.

Adam
  • 1,294
  • 11
  • 24