Kiedy mam do czynienia z AJAX, który zwracam jako JSON, jedną sztuczką, której używam, jest wykorzystanie buforowania wyjścia. Nie możesz po prostu echo lub wyprowadzić czegokolwiek, co chcesz, ponieważ zepsuje to dane JSON, więc na przykład
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.
To, co to robi, polega na zawijaniu wszelkich danych wyjściowych z twojego skryptu w dane JSON, aby ich format nie był zepsuty.
Następnie po stronie javascript możesz użyć console.log
$.post(url, input, function(data){
if(data.debug) console.log(data.debug);
});
Jeśli nie jesteś przyzwyczajony do debugowania za pomocą console.log()
, zwykle możesz nacisnąć F12
i otwórz debuger w większości przeglądarek. Następnie dane wyjściowe zostaną przesłane do "konsoli". IE9 miał mały problem z console.log()
jeśli sobie przypominam, ale nie chcę zbaczać z trasy.
UWAGA: Tylko upewnij się, że nie zostawiasz tych rzeczy w kodzie, gdy przenosisz je do produkcji, bardzo łatwo jest po prostu skomentować ten wiersz,
//$data['debug'] = $debug;
A wtedy informacje debugowania nie zostaną ujawnione w środowisku produkcyjnym. Istnieją inne sposoby automatycznego wykonania tego, ale zależy to od tego, czy programujesz lokalnie, a następnie publikujesz na serwerze. Na przykład możesz przełączyć go na $_SERVER['SERVER_ADDR'];
który będzie ::1
lub 127.0.0.1
kiedy jest lokalny. Ma to kilka wad, głównie adres serwera nie jest dostępny z interfejsu wiersza poleceń (CLI). Więc zazwyczaj powiążę to ze stałą globalną, która mówi, w jakim "trybie" jest strona (zawarta we wspólnym punkcie wejścia, zwykle 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);
Wtedy wystarczy to sprawdzić:
if(ENVIRONMENT == ENV_PRODUCTION ) $data['debug'] = $debug;
Jeśli wiesz, jak korzystać z raportowania błędów, możesz nawet powiązać się z tym za pomocą
if(ini_get('display_errors') == 1) $data['debug'] = $debug;
Który pokaże debugowanie tylko wtedy, gdy błędy wyświetlania są włączone.
Mam nadzieję, że to pomoże.
AKTUALIZUJ
Ponieważ wspomniałem o tym w komentarzach, oto przykład zapakowany w klasę (jest to uproszczona wersja, więc tego nie testowałem)
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;
}
}
Następnie, gdy tworzysz odpowiedź AJAX, po prostu owijasz ją w ten sposób (zauważ, że $result jest przekazywany przez referencję, w ten sposób nie musimy zwracać, a w przypadku wyjątku zamiast tego aktualizujemy $result w "czasie rzeczywistym" na zakończenie)
LibAjax::respond( function(&$result){
$result['data'] = 'foo';
});
Jeśli potrzebujesz przekazać dodatkowe dane do zamknięcia, nie zapomnij, że możesz użyć use
oświadczenie, takie jak to.
$otherdata = 'bar';
LibAjax::respond( function(&$result) use($otherdata){
$result['data'][] = 'foo';
$result['data'][] = $otherdata;
});
Obsługuje to przechwytywanie dowolnych danych wyjściowych i umieszcza je w debugowaniu, jeśli środowisko jest poprawne (zakomentowane). Proszę o zaimplementowanie jakiegoś zabezpieczenia, aby dane wyjściowe nie były wysyłane do klientów na produkcji, nie mogę tego wystarczająco podkreślić. Przechwytuje również wszelkie wyjątki, co powoduje błąd. Obsługuje również nagłówek i kodowanie.
Jedną z wielkich korzyści jest spójna struktura twojego JSON, będziesz wiedział (po stronie klienta), że jeśli if(data.userdata.error)
wtedy masz wyjątek na zapleczu. Daje Ci jedno miejsce do poprawiania nagłówków, kodowania JSON itp.
Jedna uwaga w PHP7 musisz lub powinieneś dodać interfejs Throwable (zamiast Exception). Jeśli chcesz przechwycić klasy Error i Exception Lub wykonaj dwa bloki catch.
Powiedzmy, że robię dużo AJAX i mam dość ciągłego pisania tego od nowa, moja rzeczywista klasa jest bardziej obszerna niż ta, ale to jest sedno tego.
Pozdrawiam.
AKTUALIZACJA1
Dzieje się tak zazwyczaj dlatego, że nie przekazujesz poprawnego nagłówka z powrotem do przeglądarki. Jeśli wyślesz (tuż przed wywołaniem json_encode)
header('Content-Type: application/json');
Dzięki temu przeglądarka wie, jaki rodzaj danych otrzymuje z powrotem. Jedną z rzeczy, o których większość ludzi zapomina, jest to, że w sieci wszystkie odpowiedzi są pisane tekstem. Nawet obrazy lub pliki do pobrania i strony internetowe. To wszystko jest tylko tekstem, co czyni ten tekst czymś wyjątkowym to Content-Type
że przeglądarka tak myśli.
Jedna rzecz do zapamiętania na temat header
jest to, że nie możesz nic wypisać przed wysłaniem nagłówków. Jednak działa to dobrze z kodem, który opublikowałem, ponieważ ten kod przechwyci wszystkie dane wyjściowe i wyśle je po wysłaniu nagłówka.
Zaktualizowałem oryginalny kod, aby miał nagłówek, miałem go w bardziej złożonej klasie, którą opublikowałem później. Ale jeśli dodasz to, powinieneś pozbyć się konieczności ręcznego parsowania JSON.
Ostatnią rzeczą, o której powinienem wspomnieć, jest sprawdzenie, czy otrzymałem JSON z powrotem lub tekst, nadal możesz uzyskać tekst w przypadku wystąpienia jakiegoś błędu przed rozpoczęciem buforowania wyjściowego.
Można to zrobić na 2 sposoby.
Jeśli Data jest ciągiem, który należy przeanalizować
$.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.
}
}
//....
}
Lub jeśli masz nagłówek i jego zawsze JSON, to jest to trochę prostsze
$.post(url, {}, function(data){
if( typeof data == 'string'){
data = {userdata : {error : data}};
}
if(data.userdata){
if( data.userdata.error){
//...etc.
}
}
//....
}
Mam nadzieję, że to pomoże!
AKTUALIZACJA2
Ponieważ ten temat pojawia się bardzo często, umieściłem zmodyfikowaną wersję powyższego kodu na moim GitHub, możesz go znaleźć tutaj.
https://github.com/ArtisticPhoenix/MISC/blob/master /AjaxWrapper/AjaxWrapper.php