Предотвратите отладку wp от загрязнения ajax в плагине


Здесь я столкнулся с проблемой, когда wp_debug замечает, что мой ответ ajax искажен.

Кодекс предлагает использовать ob_clean непосредственно перед возвращением или повторением:

Для анализа AJAX WordPress необходимо перезагрузить через admin-ajax.php скрипт, что означает, что любые ошибки PHP, возникшие при начальной загрузке страницы, также будут присутствовать при анализе AJAX. Если включена функция error_reporting, они будут переданы в выходной буфер, загрязняя ваш Ответ AJAX с сообщениями об ошибках.

Из-за этого при отладке Ajax необходимо соблюдать осторожность, так как любые возвращаемые уведомления или сообщения PHP могут запутать анализ результатов или привести к ошибке в вашем Javascript.

Один из вариантов, если вы не можете удалить сообщения и должны работать с отладкой включено, чтобы очистить буфер непосредственно перед возвратом ваших данных.

Итак, я сделал это в конце моей функции ajax ниже, но это создает ob_clean(): failed to delete buffer. No buffer to delete.

Хотя этот буфер может быть пустым, в мой ответ все еще попадают сообщения об ошибках, которые, как представляется, предполагает Кодекс, будут очищены ob_clean. Далее я использую exit() для возврата результатов, и комментарии на справочной странице предполагают, что он тоже очистит буфер. Я знаю, что в wp_config.php есть способы условно отключить wp_debug, но мне бы очень хотелось, чтобы это было обработано в самом плагине.

Пожалуйста, простите, что я включил большую часть функция ниже. Я просто не знаю, как с этим справиться.

/*
 * Process the ajax request
 * add_action('wp_ajax_title_check', 'duplicate_title_check_callback');
 * action must appear in the main plugin file, not in as separate ajax function file
 * 
 * @uses:       title_check
 * @global:     $wpdb
 * @wp-hook:    wp_ajax_title_check
 *
 */

    function duplicate_title_check_callback()
{
    global $wpdb;
    $title = $_POST['post_title'];
    $post_id = $_POST['post_id'];
    header('Content-Type: application/json');
    $sim_query = "SELECT ID FROM $wpdb->posts WHERE post_status = 'publish' AND post_type = 'venue' AND post_title LIKE '%%%s%%' AND ID != '%d'";
    $sim_results = $wpdb->get_results( $wpdb->prepare( $sim_query, like_escape($title), $post_id ) );
    if ($sim_results)
    {
        $notice = array("head" => "Whooa there! We found the following venue(s) with a similar heading:", "foot" =>"Consider deleting any duplicates and/or moving this post to the trash.");
        foreach ($sim_results as $sim_result)
        {
            $venue['title'] = get_the_title($sim_result->ID);
            $path = 'post.php?post=' . $sim_result->ID . '&action=edit';
            $venue['link'] = esc_url(admin_url($path));
            $venue['city'] =  wpcf_api_field_meta_value( 'city', $sim_result->ID );
            $venue['ID'] =  $sim_result->ID;
            $posts[] = $venue;
        }
        $return_json = array("status" => "true", "notice" => $notice, "posts"=>$posts );
    } else {
        $return_json = array("status" => "false", "notice" => "This Venue title looks unique!");
    }
    ob_clean();
    exit(json_encode($return_json)); // This should flush buffers to default point http://php.net/manual/en/function.exit.php#101204
    //echo(json_encode($return_json)); // Same result
    wp_die(); // terminate the wp instance, exit should be sufficient.
}

Я также видел: Как определить WP_DEBUG как истинный вне wp-config.php?

РЕДАКТИРОВАТЬ: После совета @czerspalace и отличной статьи @bosco это то, к чему я пришел на данный момент. В основном проблема заключалась в нескольких каскадных ошибках, но более полная реализация "более надежного" метода bosco фактически позволила бы отлавливать и регистрировать ошибки. В принципе я понимаю, но, учитывая мой опыт до сих пор, я не ожидайте, что добавление if( ob_get_length() ) ob_clean(); действительно очистит буфер, так как до сих пор он не работал. Но это шаг в правильном направлении, и, учитывая, что проверка возвращает вызов заголовка в более подходящее место, это тоже плюс. Джентльмены, благодарю вас.

function duplicate_title_check_callback()
{
    global $wpdb;
    $title = $_POST['post_title'];
    $post_id = $_POST['post_id'];
    $sim_query = "SELECT ID FROM $wpdb->posts WHERE post_status = 'publish' AND post_type = 'venue' AND post_title LIKE '%%%s%%' AND ID != '%d'";
    $sim_results = $wpdb->get_results( $wpdb->prepare( $sim_query, $wpdb->esc_like($title), $post_id ) );
    if ($sim_results)
    {
        $notice = array("head" => "Whooa there! We found the following venue(s) with a similar heading:", "foot" =>"Consider deleting any duplicates and/or moving this post to the trash.");
        foreach ($sim_results as $sim_result)
        {
            $venue['title'] = get_the_title($sim_result->ID);
            $path = 'post.php?post=' . $sim_result->ID . '&action=edit';
            $venue['link'] = esc_url(admin_url($path));
            $venue['city'] =  wpcf_api_field_meta_value( 'city', $sim_result->ID );
            $venue['ID'] =  $sim_result->ID;
            $posts[] = $venue;
        }
        $return_json = array("status" => "true", "notice" => $notice, "posts"=>$posts );
    }
    else
    {
        $return_json = array("status" => "false", "notice" => "This Venue title looks unique!");
    }
    if( ob_get_length() )
        ob_clean();
        header('Content-Type: application/json');
        // http://php.net/manual/en/function.exit.php#101204
        exit(json_encode($return_json));

}
Author: Community, 2015-04-14

1 answers

" Как я могу предотвратить попадание неожиданного вывода в браузер/вмешательство в мой ответ AJAX?"

Проблема

Что-то в вашей установке генерирует "неожиданный вывод", то есть создает контент или данные, которых не должно быть, если WordPress работает бесперебойно. Это может означать неправильно настроенную серверную среду, неправильную установку WordPress, плохое подключение к базе данных, старый плагин, тему, пытающуюся отобразить контент когда этого не должно быть, или проблемы в вашем собственном коде - суть в том, что любая ошибка, контент или уведомление, сгенерированные по какой-либо причине, появляются там, где их не должно быть.

Наиболее стабильными средами WordPress, как правило, являются те, которые не генерируют никаких посторонних выходных данных или сообщений, даже когда WP_DEBUG включен ; поэтому наилучший возможный способ действий - изучить этот неожиданный вывод, определить, что это за источник, и решить проблемы таким образом, чтобы пользователь мог получить доступ к единственный результат - это правильный, чистый ответ. В идеале производственная установка вообще не должна генерировать ошибок, предупреждений или уведомлений.

Однако в ходе разработки может быть полезно на мгновение игнорировать неожиданные результаты, поступающие из источников, к которым вы в настоящее время не обращаетесь, или которые, возможно, находятся вне вашего контроля (в случае, если источником является ответственность другого члена команды, например, или тема, с которой вы должны работать, но еще не получили ожидаемого ответа). обновление). И если в процессе производства возникнут некритические ошибки, вы, конечно, не хотите, чтобы они мешали работе конечного пользователя; лучшим способом действий было бы записать их в файл, чтобы вы могли устранить их позже.

Факт остается фактом: единственное верное решение состоит в том, чтобы найти отдельные проблемы, создающие неожиданный результат, и решать их одну за другой. Все остальное следует рассматривать как временный "взлом"

.

Быстрое решение

Чтобы ответить на ваш короче говоря, вопрос ob_clean() действительно завершится неудачей, если либо нет буфера, либо буфер пуст. Если вы все еще получаете неожиданный вывод после успешного вызова ob_clean(), подразумевается, что ваш код каким-то образом создает вывод - в предоставленном коде, возможно, вызов exit(json_encode($return_json)); или, возможно, даже неудачный вызов ob_clean() сам. Добавление проверки только для очистки уже существующего непустого буфера может быть решением, которое вы ищете (обратите внимание, разместите вызов header('Content-Type: application/json'); непосредственно перед вами выведите ответ, чтобы предотвратить удаление заголовка ob_clean() в случае вложенных буферов или любых ошибок, связанных с header()):

if( ob_get_length() )
  ob_clean();

header('Content-Type: application/json');
echo json_encode( $return_json );
exit;

Если вы все еще получаете неожиданный вывод на этом этапе, json_encode(), вероятно, задыхается, пытаясь обработать $return_json - дважды проверьте содержимое переменной и требования json_encode().

Если проблема не устранена, принудительное использование выходного буфера в сочетании с вышеперечисленным может помочь. Это скорее спекулятивный взлом, чем законный решение, однако, и лучше всего было бы избежать. Попробуйте выполнить одно из следующих действий:

  • Измените значение буферизации вывода в файле конфигурации php.ini (расположение зависит от среды сервера) и перезапустите веб-сервер: output_buffering on
  • Попытайтесь включить буферизацию вывода на уровне сценария. Добавьте следующую строку в wp-config.php: ini_set( 'output_buffering', 'on' );
    • В противном случае простой вызов ob_start() в том же файле может дать результаты, но сопряжен с риском вмешательство в более сложные среды и установки.
  • Если PHP работает как модуль Apache, вы можете включить буферизацию вывода из .htaccess файл в корневом каталоге вашей установки WordPress:

<IfModule mod_php5.c> php_value output_buffering On </IfModule>

Если ничего из вышеперечисленного не сработает, боюсь, у меня закончились идеи!


Более Надежный Взлом

Я применил нечто вроде комплексного подхода к решению этой потенциальной проблемы, работая самостоятельно плагины, принимая во внимание возможность того, что другой код, находящийся вне моего контроля, генерирует неожиданный результат, а также мой собственный - я должен признать, что он не был тщательно протестирован, но до сих пор служил мне хорошо. Несомненно, существуют более простые решения, но я намерен предложить идеи для более полезного подхода, чем тот, который предлагается в кодексе. Я совершенно уверен, что в моей собственной реализации также есть место для значительного улучшения.

PHP управляет выводом с помощью стека вложенных выходных буферов и в зависимости от того, какой сервер и расширения PHP установлены и включены (а также функции самого PHP-скрипта), дополнительные буферы могут располагаться поверх буфера PHP по умолчанию. Или может даже не быть буфера вывода (что может иметь место, если директива PHP ini output_buffering отключена, а сам скрипт его не создал).

Я должен отметить, что несколько расширений PHP используют буферы для выполнения таких действий, как перезапись URL-адресов и сжатие вывода - мое решение ниже следует использовать только для устранения неполадок и разработки, так как это почти наверняка помешает правильному функционированию этих механизмов в ваших ответах AJAX. Обычно этот эффект незначителен, но он может создавать проблемы в более сложных средах и сайтах. Взлом, описанный в Кодексе, сопряжен с аналогичным риском, хотя и меньшим, поскольку он не полностью уничтожает буферы, как это делаю я.

Итак, когда я начинаю отладку обработчика AJAX, я сначала сбрасываю содержимое любого вложенного буферы и явно удалите каждый буфер из стека, затем используйте самый верхний буфер (или создайте его, если он не существует) для захвата любых сообщений, сгенерированных в ходе обработки запроса AJAX (а также любых, содержащихся в уже существующем буфере верхнего уровня):

if( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
  $bufferContents = array();

  // Capture nested buffer contents and discard them
  while( 1 < ob_get_level() )
    $bufferContents[] = ob_get_clean();

  // Ensure that a top-level buffer is available to capture any unexpected output
  if( ! ob_get_level() )
    ob_start();
}

Массив $bufferContents затем может быть записан в журнал отладки или записан в другом месте для справки, если это необходимо.

После обработки запроса и непосредственно перед отправкой ответа я фиксирую содержимое моего нового буфера верхнего уровня, который теперь содержит любые неожиданные выходные данные, относящиеся к моему сценарию обработчика AJAX:

// If the output buffer contains data, get rid of it to prevent mucking up the JSON response
if( 0 < ( $bufferLength = ob_get_length() ) ) {
  $bufferContents = ob_end_clean();
}

header( "Content-Type: application/json" );
echo $ajaxResponse;
exit;

Еще раз, переменная $bufferContents, вероятно, содержит полезную информацию и может быть обработана по желанию.

Более конкретно, мне нравится отвечать на запросы AJAX с помощью объекта JSON - когда включена отладка, я добавляю содержимое буфера к объекту таким образом, чтобы я мог проверить его в консоли инспектора Chrome, а также сохранить его в файл независимо от включена ли отладка или нет. Например:

$ajaxResponse  = array(
  'status' => $status || 'error',
  'data'   => $response
);

// If the output buffer contains data, get rid of it to prevent mucking up the JSON response
if( 0 < ( $bufferLength = ob_get_length() ) ) {
  $bufferContents = ob_end_clean();

  // If debugging is enabled, pass any unexpected output to the client in the form of an additional 'phpBuffer' property
  if( WP_DEBUG ) {
    $ajaxResponse[ 'phpBuffer' ] => $bufferContents;
  }

  // Take note of the situation in the log files
  if( WP_DEBUG_LOG ) {
    $bufferLogFile  = plugin_dir_path( __FILE__ ) . 'debug.buffer.log';
    $bufferContents = date('m/d/Y h:i:s a', time()) . ':' . chr(10) . $bufferContents . chr(10) . chr(10);

    error_log( $bufferLength . ' characters of unexpected output were generated while processing an AJAX request in "' . plugin_dir_path( __FILE__ ) . __FILE__ . '". They have been recorded in "' . $bufferLogFile . '".';

    // Save the buffer contents to file.
    file_put_contents( $bufferLogFile, $bufferContents, FILE_APPEND );
  }
}

header( "Content-Type: application/json" );
echo json_encode( $ajaxResponse );
exit;
 2
Author: bosco, 2015-04-15 03:02:54