Quando lido com AJAX, que retorno como JSON, um truque que uso é aproveitar o buffer de saída. Você não pode simplesmente ecoar ou produzir o que quiser, porque isso atrapalhará os dados JSON, por exemplo,
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.
O que isso faz é envolver qualquer saída do seu script em seus dados JSON para que seu formato não seja confuso.
Então, no lado do javascript, você pode usar console.log
$.post(url, input, function(data){
if(data.debug) console.log(data.debug);
});
Se você não está acostumado a depurar com
console.log()
, normalmente você pode pressionar F12
e abra o depurador na maioria dos navegadores. Então lá a saída será enviada para o "console". O IE9 teve um pequeno problema com console.log()
se bem me lembro, mas não quero ir muito longe. OBSERVAÇÃO: Apenas certifique-se de não deixar essas coisas no código quando você movê-lo para produção, é muito simples apenas comentar esta linha,
//$data['debug'] = $debug;
E então suas informações de depuração não serão expostas na produção. Existem outras maneiras de fazer isso automaticamente, mas depende se você faz o desenvolvimento local e depois publica no servidor. Por exemplo, você pode ativá-lo no
$_SERVER['SERVER_ADDR'];
que será ::1
ou 127.0.0.1
quando é local. Isso tem algumas desvantagens, principalmente o endereço do servidor não está disponível na interface de linha de comando (CLI). Então, normalmente eu vou amarrá-lo em uma constante global que diz em que "modo" o site está (incluído no ponto de entrada comum, normalmente 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);
Então é uma questão simples de verificar:
if(ENVIRONMENT == ENV_PRODUCTION ) $data['debug'] = $debug;
Se você sabe como usar o relatório de erros, pode até vincular isso usando
if(ini_get('display_errors') == 1) $data['debug'] = $debug;
Que só mostrará a depuração quando os erros de exibição estiverem ativados.
Espero que ajude.
ATUALIZAÇÃO
Porque eu mencionei isso nos comentários, aqui está um exemplo dele envolto em uma classe (esta é uma versão simplificada, então não testei isso)
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;
}
}
Então, quando você faz uma resposta AJAX, basta envolvê-la assim (observe que $ resultado é passado por referência, dessa forma não precisamos retornar e, no caso de uma exceção, atualizamos $ resultado em "tempo real" de na conclusão)
LibAjax::respond( function(&$result){
$result['data'] = 'foo';
});
Se você precisar passar dados adicionais para o encerramento, não esqueça que você pode usar o
use
declaração, assim. $otherdata = 'bar';
LibAjax::respond( function(&$result) use($otherdata){
$result['data'][] = 'foo';
$result['data'][] = $otherdata;
});
Sandbox
Isso lida com a captura de qualquer saída e a coloca em depuração, se o ambiente estiver correto (comentado). Por favor, certifique-se de implementar algum tipo de proteção para que a saída não seja enviada aos clientes em produção, não posso enfatizar isso o suficiente. Ele também captura todas as exceções que o colocam em erro. E também lida com o cabeçalho e a codificação.
Um grande benefício para isso é uma estrutura consistente para seu JSON, você saberá (no lado do cliente) que se
if(data.userdata.error)
então você tem uma exceção no back-end. Dá-lhe um lugar para ajustar seus cabeçalhos, codificação JSON etc ... Uma nota no PHP7 você terá que ou deve adicionar a interface Throwable (em vez de Exception). Se você quiser pegar as classes Error e Exception Ou faça dois blocos catch.
Vamos apenas dizer que eu faço muito AJAX e fiquei cansado de reescrever isso o tempo todo, minha aula real é mais extensa do que isso, mas essa é a essência.
Felicidades.
ATUALIZAÇÃO1
Isso geralmente ocorre porque você não está passando o cabeçalho correto de volta para o navegador. Se você enviar (logo antes de chamar json_encode)
header('Content-Type: application/json');
Isso apenas permite que o navegador saiba que tipo de dados está recebendo de volta. Uma coisa que a maioria das pessoas esquece é que na web todas as respostas são feitas em texto. Mesmo imagens ou download de arquivos e páginas da web. É tudo apenas texto, o que torna esse texto em algo especial é o
Content-Type
que o navegador pensa que é. Uma coisa a ser observada sobre
header
é que você não pode produzir nada antes de enviar os cabeçalhos. No entanto, isso funciona bem com o código que postei porque esse código capturará toda a saída e a enviará após o envio do cabeçalho. Atualizei o código original para ter o cabeçalho, tinha na classe mais complexa que postei depois. Mas se você adicionar isso, deve se livrar da necessidade de analisar manualmente o JSON.
Uma última coisa que devo mencionar que faço é verificar se recebi JSON de volta ou texto, você ainda pode obter texto no caso de ocorrer algum erro antes que o buffer de saída seja iniciado.
Existem 2 maneiras de fazer isso.
Se Data for uma string que precisa ser analisada
$.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.
}
}
//....
}
Ou se você tem o cabeçalho e é sempre JSON, é um pouco mais simples
$.post(url, {}, function(data){
if( typeof data == 'string'){
data = {userdata : {error : data}};
}
if(data.userdata){
if( data.userdata.error){
//...etc.
}
}
//....
}
Espero que ajude!
ATUALIZAÇÃO2
Como esse tópico aparece muito, coloquei uma versão modificada do código acima no meu GitHub, você encontra aqui.
https://github.com/ArtisticPhoenix/MISC/blob/master /AjaxWrapper/AjaxWrapper.php