Когато се занимавам с AJAX, който връщам като JSON, един трик, който използвам, е да се възползвам от изходното буфериране. Не можете просто да повторите или изведете всичко, което искате, защото това ще обърка JSON данните, така че за пример,
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.
Това, което прави, е да обвие всеки изход от вашия скрипт във вашите JSON данни, така че форматът им да не бъде объркан.
След това от страна на javascript можете да използвате console.log
$.post(url, input, function(data){
if(data.debug) console.log(data.debug);
});
Ако не сте свикнали да отстранявате грешки с console.log()
, обикновено можете да натиснете F12
и отворете програмата за отстраняване на грешки в повечето браузъри. Тогава там изходът ще бъде изпратен до "конзолата". IE9 имаше малък проблем с console.log()
ако си спомням, но не искам да се отклонявам много от пътя.
ЗАБЕЛЕЖКА: Просто се уверете, че не оставяте тези неща в кода, когато го преместите в производство, много е лесно просто да коментирате този ред,
//$data['debug'] = $debug;
И тогава вашата информация за отстраняване на грешки няма да бъде изложена в производството. Има и други начини за автоматично извършване на това, но зависи от това дали правите локална разработка и след това публикувате на сървъра. Например можете да го включите на $_SERVER['SERVER_ADDR'];
което ще бъде ::1
или 127.0.0.1
когато е местно. Това има няколко недостатъка, главно адресът на сървъра не е достъпен от интерфейса на командния ред (CLI). Така че обикновено ще го свържа с глобална константа, която казва в какъв „режим“ е сайтът (включен в общата входна точка, обикновено 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);
Тогава е лесно да го проверите:
if(ENVIRONMENT == ENV_PRODUCTION ) $data['debug'] = $debug;
Ако знаете как да използвате докладването за грешки, можете дори да се свържете с това с помощта на
if(ini_get('display_errors') == 1) $data['debug'] = $debug;
Което ще покаже отстраняването на грешки само когато е включено показване на грешки.
Надявам се, че това помага.
АКТУАЛИЗАЦИЯ
Тъй като го споменах в коментарите, ето пример за него, обвит в клас (това е опростена версия, така че не съм го тествал)
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;
}
}
След това, когато правите AJAX отговор, вие просто го обвивате по този начин (имайте предвид, че $result се предава по референция, по този начин не трябва да правим return, а в случай на изключение актуализираме $result в "реално време" вместо това от при завършване)
LibAjax::respond( function(&$result){
$result['data'] = 'foo';
});
Ако трябва да предадете допълнителни данни в затварянето, не забравяйте, че можете да използвате use
изявление като това.
$otherdata = 'bar';
LibAjax::respond( function(&$result) use($otherdata){
$result['data'][] = 'foo';
$result['data'][] = $otherdata;
});
Това обработва улавянето на всеки изход и го поставя в отстраняване на грешки, ако средата е правилна (коментирано). Моля, уверете се, че прилагате някакъв вид защита, така че изходът да не се изпраща на клиентите при производство, не мога да подчертая това достатъчно. Той също така улавя всякакви изключения, поставя го в грешка. И също така обработва заглавката и кодирането.
Една голяма полза от това е последователната структура на вашия JSON, ще знаете (от страна на клиента), че ако if(data.userdata.error)
тогава имате изключение на задния край. Дава ви едно място за настройване на вашите заглавки, JSON кодиране и т.н...
Една забележка в PHP7 ще трябва или трябва да добавите интерфейса Throwable (вместо Exception). Ако искате да прихванете класове за грешка и изключение Или направете два блока за улавяне.
Нека просто кажем, че правя много AJAX и ми писна да пренаписвам това през цялото време, действителният ми клас е по-обширен от този, но това е същината.
Наздраве.
АКТУАЛИЗАЦИЯ1
Това обикновено е, защото не предавате правилната заглавка обратно към браузъра. Ако изпратите (точно преди да извикате json_encode)
header('Content-Type: application/json');
Това просто позволява на браузъра да знае какъв тип данни получава обратно. Едно нещо, което повечето хора забравят е, че в мрежата всички отговори се правят в текст. Дори изображения или изтегляне на файлове и уеб страници. Всичко е просто текст, това, което прави този текст нещо специално, е Content-Type
което браузърът смята, че е.
Едно нещо, което трябва да отбележите относно header
е, че не можете да изведете нищо, преди да изпратите заглавките. Това обаче работи добре с кода, който публикувах, защото този код ще улови целия изход и ще го изпрати след изпращане на заглавката.
Актуализирах оригиналния код, за да има заглавката, имах го в по-сложния клас, който публикувах по-късно. Но ако добавите, че в него трябва да се отървете от необходимостта от ръчно анализиране на JSON.
Едно последно нещо, което трябва да спомена, е да проверя дали съм получил обратно JSON или текст, пак можете да получите текст в случай, че възникне някаква грешка, преди да започне буферирането на изхода.
Има 2 начина да направите това.
Ако данните са низ, който трябва да бъде анализиран
$.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.
}
}
//....
}
Или ако имате заглавката и тя винаги е JSON, тогава е малко по-просто
$.post(url, {}, function(data){
if( typeof data == 'string'){
data = {userdata : {error : data}};
}
if(data.userdata){
if( data.userdata.error){
//...etc.
}
}
//....
}
Надяваме се, че това помага!
АКТУАЛИЗАЦИЯ 2
Тъй като тази тема се появява често, поставих модифицирана версия на горния код в моя GitHub, можете да я намерите тук.
https://github.com/ArtisticPhoenix/MISC/blob/master /AjaxWrapper/AjaxWrapper.php