När jag hanterar AJAX, som jag återkommer som JSON, är ett knep jag använder att dra fördel av outputbuffring. Du kan inte bara eka eller mata ut vad du vill eftersom det kommer att förstöra JSON-data, så till exempel,
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.
Vad detta gör är att linda in alla utdata från ditt skript i din JSON-data så att formatet inte förstörs.
Sedan på javascript-sidan kan du använda console.log
$.post(url, input, function(data){
if(data.debug) console.log(data.debug);
});
Om du inte är van vid att felsöka med console.log()
, kan du vanligtvis trycka på F12
och öppna felsökaren i de flesta webbläsare. Sedan kommer utdata där att skickas till "konsolen". IE9 hade lite problem med console.log()
om jag minns det, men jag vill inte gå för långt bort.
OBS: Se bara till att inte lämna det här i koden när du flyttar det till produktion, det är väldigt enkelt att bara kommentera den här raden,
//$data['debug'] = $debug;
Och då kommer din felsökningsinformation inte att exponeras i produktionen. Det finns andra sätt att automatiskt göra detta, men det beror på om du utvecklar lokalt och sedan publicerar på servern. Du kan till exempel slå på $_SERVER['SERVER_ADDR'];
vilket blir ::1
eller 127.0.0.1
när det är lokalt. Detta har några nackdelar, främst serveradressen är inte tillgänglig från Command Line Interface (CLI). Så vanligtvis kopplar jag det till en global konstant som säger vilket "läge" webbplatsen är i (ingår i den gemensamma ingångspunkten, vanligtvis 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);
Då är det enkelt att kontrollera det:
if(ENVIRONMENT == ENV_PRODUCTION ) $data['debug'] = $debug;
Om du vet hur man använder felrapportering kan du till och med ansluta till det med
if(ini_get('display_errors') == 1) $data['debug'] = $debug;
Vilket bara visar felsökningen när skärmfel är på.
Hoppas det hjälper.
UPPDATERA
Eftersom jag nämnde det i kommentarerna, här är ett exempel på det insvept i en klass (detta är en förenklad version, så jag testade inte detta)
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;
}
}
När du sedan gör ett AJAX-svar slår du bara in det så här (observera att $result är passerat genom referens, på detta sätt behöver vi inte returnera, och i fallet med ett undantag uppdaterar vi $result i "realtid" istället av vid slutförande)
LibAjax::respond( function(&$result){
$result['data'] = 'foo';
});
Om du behöver skicka ytterligare data till stängningen, glöm inte att du kan använda use
uttalande, så här.
$otherdata = 'bar';
LibAjax::respond( function(&$result) use($otherdata){
$result['data'][] = 'foo';
$result['data'][] = $otherdata;
});
Detta hanterar att fånga alla utdata och placerar den i felsökning, om miljön är korrekt (kommenterad). Vänligen se till att implementera någon form av skydd så att resultatet inte skickas till kunder vid produktion, jag kan inte betona det tillräckligt. Den fångar också upp eventuella undantag som gör det felaktigt. Och den hanterar även rubriken och kodningen.
En stor fördel med detta är konsekvent struktur för din JSON, du kommer att veta (på klientsidan) att om if(data.userdata.error)
då har du ett undantag på baksidan. Det ger dig ett ställe att justera dina rubriker, JSON-kodning etc...
En anteckning i PHP7 måste du eller bör lägga till Throwable-gränssnittet (istället för Exception). Om du vill fånga klasserna Error och Exception Eller gör två catch-blocks.
Låt oss bara säga att jag gör mycket AJAX och blev trött på att skriva om detta hela tiden, min faktiska klass är mer omfattande än den här, men det är kärnan i det.
Skål.
UPPDATERING1
Detta beror vanligtvis på att du inte skickar tillbaka rätt rubrik till webbläsaren. Om du skickar (strax innan du ringer json_encode)
header('Content-Type: application/json');
Detta låter bara webbläsaren veta vilken typ av data den får tillbaka. En sak de flesta glömmer är att på webben görs alla svar i text. Även bilder eller filnedladdning och webbsidor. Allt är bara text, det som gör den texten till något speciellt är Content-Type
som webbläsaren tror att det är.
En sak att notera om header
är att du inte kan mata ut något innan du skickar rubrikerna. Det här spelar dock bra med koden jag postade eftersom den koden kommer att fånga all utdata och skicka den efter att rubriken har skickats.
Jag uppdaterade den ursprungliga koden för att ha rubriken, jag hade den i den mer komplexa klassen jag postade senare. Men om du lägger till det borde det bli av med behovet av att manuellt tolka JSON.
En sista sak jag bör nämna att jag gör är att kontrollera om jag fick JSON tillbaka eller text, du kan fortfarande få text i händelse av att något fel inträffar innan utdatabuffringen startas.
Det finns två sätt att göra detta.
Om Data är en sträng som behöver tolkas
$.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.
}
}
//....
}
Eller om du har rubriken och alltid JSON, så är det lite enklare
$.post(url, {}, function(data){
if( typeof data == 'string'){
data = {userdata : {error : data}};
}
if(data.userdata){
if( data.userdata.error){
//...etc.
}
}
//....
}
Hoppas det hjälper!
UPPDATERING 2
Eftersom detta ämne kommer upp mycket, har jag lagt en modifierad version av ovanstående kod på min GitHub, du kan hitta den här.
https://github.com/ArtisticPhoenix/MISC/blob/master /AjaxWrapper/AjaxWrapper.php