Når jeg beskæftiger mig med AJAX, som jeg vender tilbage som JSON, er et trick, jeg bruger, at drage fordel af outputbuffering. Du kan ikke bare ekko eller udsende noget, du ønsker, fordi det vil ødelægge JSON-dataene, så for eksempel,
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.
Hvad dette gør, er at pakke ethvert output fra dit script ind i dine JSON-data, så dets format ikke bliver rodet.
Så på javascript-siden kan du bruge console.log
$.post(url, input, function(data){
if(data.debug) console.log(data.debug);
});
Hvis du ikke er vant til at fejlfinde med console.log()
, kan du normalt trykke på F12
og åbn debuggeren i de fleste browsere. Derefter vil outputtet blive sendt til "konsollen". IE9 havde lidt af et problem med console.log()
hvis jeg husker det, men jeg vil ikke gå for langt væk fra sporet.
BEMÆRK: Bare sørg for ikke at efterlade disse ting i koden, når du flytter det til produktion, det er meget nemt at bare kommentere denne linje ud,
//$data['debug'] = $debug;
Og så vil dine fejlfindingsoplysninger ikke blive afsløret i produktionen. Der er andre måder at gøre dette automatisk på, men det afhænger af om du laver udvikling lokalt og derefter publicerer til serveren. For eksempel kan du slå det på $_SERVER['SERVER_ADDR'];
som vil være ::1
eller 127.0.0.1
når det er lokalt. Dette har et par ulemper, primært serveradressen er ikke tilgængelig fra Command Line Interface (CLI). Så typisk vil jeg binde det til en global konstant, der siger, hvilken "tilstand" webstedet er i (inkluderet i det fælles indgangspunkt, typisk 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);
Så er det en simpel sag at tjekke det:
if(ENVIRONMENT == ENV_PRODUCTION ) $data['debug'] = $debug;
Hvis du ved, hvordan du bruger fejlrapportering, kan du endda tilslutte dig det ved at bruge
if(ini_get('display_errors') == 1) $data['debug'] = $debug;
Som kun vil vise fejlretningen, når skærmfejl er aktiveret.
Håber det hjælper.
OPDATERING
Fordi jeg nævnte det i kommentarerne, er her et eksempel på det pakket ind i en klasse (dette er en forenklet version, så jeg testede ikke dette)
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;
}
}
Så når du laver et AJAX-svar, pakker du det bare sådan her (bemærk $result er pass by reference, på denne måde behøver vi ikke at returnere, og i tilfælde af en undtagelse opdaterer vi $result i "realtid" i stedet for af ved afslutning)
LibAjax::respond( function(&$result){
$result['data'] = 'foo';
});
Hvis du har brug for at videregive yderligere data til lukningen, så glem ikke, at du kan bruge use
udtalelse, som denne.
$otherdata = 'bar';
LibAjax::respond( function(&$result) use($otherdata){
$result['data'][] = 'foo';
$result['data'][] = $otherdata;
});
Dette håndterer at fange ethvert output og sætter det i debug, hvis miljøet er korrekt (kommenteret ud). Sørg venligst for at implementere en form for beskyttelse, så outputtet ikke sendes til kunder i produktionen, det kan jeg ikke understrege nok. Det fanger også eventuelle undtagelser, der sætter det i fejl. Og den håndterer også headeren og kodningen.
En stor fordel ved dette er ensartet struktur til din JSON, du vil vide (på klientsiden), at hvis if(data.userdata.error)
så har du en undtagelse på bagenden. Det giver dig ét sted at justere dine overskrifter, JSON-kodning osv...
En note i PHP7 skal du eller bør tilføje Throwable-grænsefladen (i stedet for Undtagelse). Hvis du vil fange fejl- og undtagelsesklasser Eller lav to catch-blokke.
Lad os bare sige, at jeg laver meget AJAX og er blevet træt af at omskrive dette hele tiden, min faktiske klasse er mere omfattende end dette, men det er kernen i det.
Skål.
OPDATERING 1
Dette skyldes typisk, at du ikke sender den korrekte overskrift tilbage til browseren. Hvis du sender (lige før du ringer til json_encode)
header('Content-Type: application/json');
Dette lader blot browseren vide, hvilken type data den får tilbage. En ting, de fleste glemmer, er, at alle svar på nettet sker i tekst. Selv billeder eller fildownload og websider. Det hele er bare tekst, det der gør den tekst til noget særligt er Content-Type
som browseren tror, det er.
En ting at bemærke om header
er, at du ikke kan udlæse noget, før du sender headerne. Dette spiller dog godt sammen med den kode, jeg postede, fordi den kode vil fange alt output og sende det, efter at headeren er sendt.
Jeg opdaterede den originale kode til at have overskriften, jeg havde den i den mere komplekse klasse, jeg postede senere. Men hvis du tilføjer det, skulle det slippe af med behovet for manuelt at parse JSON.
En sidste ting, jeg bør nævne, at jeg gør, er at tjekke, om jeg fik JSON tilbage eller tekst, du kan stadig få tekst i tilfælde af, at der opstår en fejl, før outputbufferen startes.
Der er 2 måder at gøre dette på.
Hvis Data er en streng, der skal parses
$.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 hvis du har headeren og dens altid JSON, så er det lidt enklere
$.post(url, {}, function(data){
if( typeof data == 'string'){
data = {userdata : {error : data}};
}
if(data.userdata){
if( data.userdata.error){
//...etc.
}
}
//....
}
Håber det hjælper!
OPDATERING 2
Fordi dette emne kommer meget op, har jeg lagt en modificeret version af ovenstående kode på min GitHub, du kan finde den her.
https://github.com/ArtisticPhoenix/MISC/blob/master /AjaxWrapper/AjaxWrapper.php