Quando mi occupo di AJAX, che restituisco come JSON, un trucco che uso è sfruttare il buffering dell'output. Non puoi semplicemente fare eco o emettere tutto ciò che vuoi perché rovinerebbe i dati JSON, quindi, ad esempio,
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.
Ciò che fa è racchiudere qualsiasi output dello script nei tuoi dati JSON in modo che il suo formato non sia incasinato.
Quindi sul lato javascript puoi usare console.log
$.post(url, input, function(data){
if(data.debug) console.log(data.debug);
});
Se non sei abituato a eseguire il debug con console.log()
, di solito puoi premere F12
e apri il debugger nella maggior parte dei browser. Quindi lì l'output verrà inviato alla "console". IE9 ha avuto un piccolo problema con console.log()
se ricordo bene, ma non voglio andare troppo fuori strada.
NOTA: Assicurati solo di non lasciare queste cose nel codice quando lo sposti in produzione, è molto semplice commentare questa riga,
//$data['debug'] = $debug;
E quindi le tue informazioni di debug non verranno esposte in produzione. Esistono altri modi per farlo automaticamente, ma dipende se si esegue lo sviluppo in locale e quindi si pubblica sul server. Ad esempio puoi attivarlo su $_SERVER['SERVER_ADDR'];
che sarà ::1
o 127.0.0.1
quando è locale. Ciò presenta alcuni inconvenienti, principalmente l'indirizzo del server non è disponibile dalla Command Line Interface (CLI). Quindi in genere lo legherò a una costante globale che dice in quale "modalità" si trova il sito (incluso nel punto di ingresso comune, in genere 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);
Quindi è semplice verificarlo:
if(ENVIRONMENT == ENV_PRODUCTION ) $data['debug'] = $debug;
Se sai come utilizzare la segnalazione degli errori, puoi anche collegarla utilizzando
if(ini_get('display_errors') == 1) $data['debug'] = $debug;
Che mostrerà il debug solo quando gli errori di visualizzazione sono attivi.
Spero di esserti stato d'aiuto.
AGGIORNAMENTO
Poiché l'ho menzionato nei commenti, ecco un esempio di esso racchiuso in una classe (questa è una versione semplificata, quindi non l'ho testata)
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;
}
}
Quindi, quando fai una risposta AJAX, la avvolgi semplicemente in questo modo (nota $result è passato per riferimento, in questo modo non dobbiamo restituire e, nel caso di un'eccezione, aggiorniamo $result in "tempo reale" invece di al termine)
LibAjax::respond( function(&$result){
$result['data'] = 'foo';
});
Se hai bisogno di trasferire dati aggiuntivi nella chiusura non dimenticare che puoi usare il use
dichiarazione, come questa.
$otherdata = 'bar';
LibAjax::respond( function(&$result) use($otherdata){
$result['data'][] = 'foo';
$result['data'][] = $otherdata;
});
Questo gestisce la cattura di qualsiasi output e lo mette in debug, se l'ambiente è corretto (commentato). Per favore, assicurati di implementare un qualche tipo di protezione in modo che l'output non venga inviato ai clienti in produzione, non posso sottolinearlo abbastanza. Cattura anche eventuali eccezioni e lo mette in errore. E gestisce anche l'intestazione e la codifica.
Un grande vantaggio di questo è la struttura coerente del tuo JSON, saprai (sul lato client) che se if(data.userdata.error)
quindi hai un'eccezione sul back-end. Ti dà un posto per modificare le tue intestazioni, la codifica JSON ecc...
Una nota in PHP7 dovrai o dovresti aggiungere l'interfaccia Throwable (invece di Exception). Se vuoi catturare le classi Error ed Exception Oppure esegui due catch block.
Diciamo solo che faccio molto AJAX e mi stanco di riscriverlo tutto il tempo, la mia lezione attuale è più ampia di questa, ma questo è il succo della questione.
Saluti.
AGGIORNAMENTO1
Ciò è in genere dovuto al fatto che non stai ritrasmettendo l'intestazione corretta al browser. Se invii (poco prima di chiamare json_encode)
header('Content-Type: application/json');
Questo consente al browser di sapere che tipo di dati sta recuperando. Una cosa che la maggior parte delle persone dimentica è che sul web tutte le risposte sono scritte. Anche immagini o download di file e pagine web. È tutto solo testo, ciò che rende quel testo qualcosa di speciale è il Content-Type
che il browser pensa che sia.
Una cosa da notare su header
è che non puoi produrre nulla prima di inviare le intestazioni. Tuttavia questo funziona bene con il codice che ho pubblicato perché quel codice catturerà tutto l'output e lo invierà dopo l'invio dell'intestazione.
Ho aggiornato il codice originale per avere l'intestazione, l'ho avuto nella classe più complessa che ho pubblicato in seguito. Ma se lo aggiungi al suo interno, dovresti eliminare la necessità di analizzare manualmente il JSON.
Un'ultima cosa che dovrei menzionare che faccio è controllare se ho ricevuto JSON indietro o testo, potresti comunque ricevere testo nel caso in cui si verifichi qualche errore prima che venga avviato il buffering dell'output.
Ci sono 2 modi per farlo.
Se Data è una stringa che deve essere analizzata
$.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.
}
}
//....
}
Oppure, se hai l'intestazione ed è sempre JSON, è un po' più semplice
$.post(url, {}, function(data){
if( typeof data == 'string'){
data = {userdata : {error : data}};
}
if(data.userdata){
if( data.userdata.error){
//...etc.
}
}
//....
}
Spero di esserti stato d'aiuto!
AGGIORNAMENTO2
Dato che questo argomento viene spesso sollevato, ho inserito una versione modificata del codice sopra sul mio GitHub, puoi trovarla qui.
https://github.com/ArtisticPhoenix/MISC/blob/master /AjaxWrapper/AjaxWrapper.php