/** * Collect various debugging information and return it in an associative array * * @return array */ function get_debug_info() { /** @var wpdb $wpdb */ global $wpdb; //Collect some information that's useful for debugging $debug = array(); //PHP version. Any one is fine as long as WP supports it. $debug[__('PHP version', 'broken-link-checker')] = array('state' => 'ok', 'value' => phpversion()); //MySQL version $debug[__('MySQL version', 'broken-link-checker')] = array('state' => 'ok', 'value' => $wpdb->db_version()); //CURL presence and version if (function_exists('curl_version')) { $version = curl_version(); if (version_compare($version['version'], '7.16.0', '<=')) { $data = array('state' => 'warning', 'value' => $version['version'], 'message' => __('You have an old version of CURL. Redirect detection may not work properly.', 'broken-link-checker')); } else { $data = array('state' => 'ok', 'value' => $version['version']); } } else { $data = array('state' => 'warning', 'value' => __('Not installed', 'broken-link-checker')); } $debug[__('CURL version', 'broken-link-checker')] = $data; //Snoopy presence if (class_exists('Snoopy') || file_exists(ABSPATH . WPINC . '/class-snoopy.php')) { $data = array('state' => 'ok', 'value' => __('Installed', 'broken-link-checker')); } else { //No Snoopy? This should never happen, but if it does we *must* have CURL. if (function_exists('curl_init')) { $data = array('state' => 'ok', 'value' => __('Not installed', 'broken-link-checker')); } else { $data = array('state' => 'error', 'value' => __('Not installed', 'broken-link-checker'), 'message' => __('You must have either CURL or Snoopy installed for the plugin to work!', 'broken-link-checker')); } } $debug['Snoopy'] = $data; //Safe_mode status if (blcUtility::is_safe_mode()) { $debug['Safe mode'] = array('state' => 'warning', 'value' => __('On', 'broken-link-checker'), 'message' => __('Redirects may be detected as broken links when safe_mode is on.', 'broken-link-checker')); } else { $debug['Safe mode'] = array('state' => 'ok', 'value' => __('Off', 'broken-link-checker')); } //Open_basedir status if (blcUtility::is_open_basedir()) { $debug['open_basedir'] = array('state' => 'warning', 'value' => sprintf(__('On ( %s )', 'broken-link-checker'), ini_get('open_basedir')), 'message' => __('Redirects may be detected as broken links when open_basedir is on.', 'broken-link-checker')); } else { $debug['open_basedir'] = array('state' => 'ok', 'value' => __('Off', 'broken-link-checker')); } //Default PHP execution time limit $debug['Default PHP execution time limit'] = array('state' => 'ok', 'value' => sprintf(__('%s seconds'), ini_get('max_execution_time'))); //Resynch flag. $debug['Resynch. flag'] = array('state' => 'ok', 'value' => sprintf('%d', $this->conf->options['need_resynch'] ? '1 (resynch. required)' : '0 (resynch. not required)')); //Synch records $synch_records = intval($wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}blc_synch")); $data = array('state' => 'ok', 'value' => sprintf('%d', $synch_records)); if ($synch_records == 0) { $data['state'] = 'warning'; $data['message'] = __('If this value is zero even after several page reloads you have probably encountered a bug.', 'broken-link-checker'); } $debug['Synch. records'] = $data; //Total links and instances (including invalid ones) $all_links = intval($wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}blc_links")); $all_instances = intval($wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}blc_instances")); //Show the number of unparsed containers. Useful for debugging. For performance, //this is only shown when we have no links/instances yet. if ($all_links == 0 && $all_instances == 0) { $unparsed_items = intval($wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}blc_synch WHERE synched=0")); $debug['Unparsed items'] = array('state' => 'warning', 'value' => $unparsed_items); } //Links & instances if ($all_links > 0 && $all_instances > 0) { $debug['Link records'] = array('state' => 'ok', 'value' => sprintf('%d (%d)', $all_links, $all_instances)); } else { $debug['Link records'] = array('state' => 'warning', 'value' => sprintf('%d (%d)', $all_links, $all_instances)); } //Installation log $logger = new blcCachedOptionLogger('blc_installation_log'); $installation_log = $logger->get_messages(); if (!empty($installation_log)) { $debug['Installation log'] = array('state' => $this->conf->options['installation_complete'] ? 'ok' : 'error', 'value' => implode("<br>\n", $installation_log)); } else { $debug['Installation log'] = array('state' => 'warning', 'value' => 'No installation log found found.'); } return $debug; }
function check($url, $use_get = false) { $this->last_headers = ''; $url = $this->clean_url($url); $result = array('broken' => false); $log = ''; //Get the BLC configuration. It's used below to set the right timeout values and such. $conf = blc_get_configuration(); //Init curl. $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->urlencodefix($url)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //Masquerade as Internet explorer //$ua = 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)'; $ua = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)'; curl_setopt($ch, CURLOPT_USERAGENT, $ua); //Add a semi-plausible referer header to avoid tripping up some bot traps curl_setopt($ch, CURLOPT_REFERER, get_option('home')); //Redirects don't work when safe mode or open_basedir is enabled. if (!blcUtility::is_safe_mode() && !blcUtility::is_open_basedir()) { curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); } //Set maximum redirects curl_setopt($ch, CURLOPT_MAXREDIRS, 10); //Set the timeout curl_setopt($ch, CURLOPT_TIMEOUT, $conf->options['timeout']); //Set the proxy configuration. The user can provide this in wp-config.php if (defined('WP_PROXY_HOST')) { curl_setopt($ch, CURLOPT_PROXY, WP_PROXY_HOST); } if (defined('WP_PROXY_PORT')) { curl_setopt($ch, CURLOPT_PROXYPORT, WP_PROXY_PORT); } if (defined('WP_PROXY_USERNAME')) { $auth = WP_PROXY_USERNAME; if (defined('WP_PROXY_PASSWORD')) { $auth .= ':' . WP_PROXY_PASSWORD; } curl_setopt($ch, CURLOPT_PROXYUSERPWD, $auth); } //Make CURL return a valid result even if it gets a 404 or other error. curl_setopt($ch, CURLOPT_FAILONERROR, false); $nobody = !$use_get; //Whether to send a HEAD request (the default) or a GET request $parts = @parse_url($url); if ($parts['scheme'] == 'https') { curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); //Required to make HTTPS URLs work. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); //Likewise. $nobody = false; //Can't use HEAD with HTTPS. } if ($nobody) { //If possible, use HEAD requests for speed. curl_setopt($ch, CURLOPT_NOBODY, true); } else { //If we must use GET at least limit the amount of downloaded data. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Range: bytes=0-2048')); //2 KB } //Register a callback function which will process the HTTP header(s). //It can be called multiple times if the remote server performs a redirect. curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$this, 'read_header')); //Execute the request curl_exec($ch); $info = curl_getinfo($ch); curl_close($ch); //Store the results $result['http_code'] = intval($info['http_code']); $result['timeout'] = $result['http_code'] == 0; //If the code is 0 then it's probably a timeout $result['final_url'] = $info['url']; $result['request_duration'] = $info['total_time']; $result['redirect_count'] = $info['redirect_count']; //Determine if the link counts as "broken" $result['broken'] = $this->is_error_code($result['http_code']) || $result['timeout']; if ($nobody && $result['broken']) { //The site in question might be expecting GET instead of HEAD, so lets retry the request //using the GET verb. return $this->check($url, true); //Note : normally a server that doesn't allow HEAD requests on a specific resource *should* //return "405 Method Not Allowed". Unfortunately, there are sites that return 404 or //another, even more general, error code instead. So just checking for 405 wouldn't be enough. } //When safe_mode or open_basedir is enabled CURL will be forbidden from following redirects, //so redirect_count will be 0 for all URLs. As a workaround, set it to 1 when the HTTP //response codes indicates a redirect but redirect_count is zero. //Note to self : Extracting the Location header might also be helpful. if ($result['redirect_count'] == 0 && in_array($result['http_code'], array(301, 302, 303, 307))) { $result['redirect_count'] = 1; } //Build the log from HTTP code and headers. //TODO: Put some kind of a color-coded error explanation at the top of the log, not a cryptic HTTP code. $log .= '=== '; if ($result['http_code']) { $log .= sprintf(__('HTTP code : %d', 'broken-link-checker'), $result['http_code']); } else { $log .= __('(No response)', 'broken-link-checker'); } $log .= " ===\n\n"; $log .= $this->last_headers; if ($result['broken'] && $result['timeout']) { $log .= "\n(" . __("Most likely the connection timed out or the domain doesn't exist.", 'broken-link-checker') . ')'; } $result['log'] = $log; //The hash should contain info about all pieces of data that pertain to determining if the //link is working. $result['result_hash'] = implode('|', array($result['http_code'], $result['broken'] ? 'broken' : '0', $result['timeout'] ? 'timeout' : '0', md5($result['final_url']))); return $result; }
function check($url, $use_get = false) { $this->last_headers = ''; $url = $this->clean_url($url); $result = array('broken' => false); $log = ''; //Get the BLC configuration. It's used below to set the right timeout values and such. $conf = blc_get_configuration(); //Init curl. $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->urlencodefix($url)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //Masquerade as Internet explorer //$ua = 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)'; $ua = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)'; curl_setopt($ch, CURLOPT_USERAGENT, $ua); //Add a semi-plausible referer header to avoid tripping up some bot traps curl_setopt($ch, CURLOPT_REFERER, home_url()); //Redirects don't work when safe mode or open_basedir is enabled. if (!blcUtility::is_safe_mode() && !blcUtility::is_open_basedir()) { curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); } //Set maximum redirects curl_setopt($ch, CURLOPT_MAXREDIRS, 10); //Set the timeout curl_setopt($ch, CURLOPT_TIMEOUT, $conf->options['timeout']); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $conf->options['timeout']); //Set the proxy configuration. The user can provide this in wp-config.php if (defined('WP_PROXY_HOST')) { curl_setopt($ch, CURLOPT_PROXY, WP_PROXY_HOST); } if (defined('WP_PROXY_PORT')) { curl_setopt($ch, CURLOPT_PROXYPORT, WP_PROXY_PORT); } if (defined('WP_PROXY_USERNAME')) { $auth = WP_PROXY_USERNAME; if (defined('WP_PROXY_PASSWORD')) { $auth .= ':' . WP_PROXY_PASSWORD; } curl_setopt($ch, CURLOPT_PROXYUSERPWD, $auth); } //Make CURL return a valid result even if it gets a 404 or other error. curl_setopt($ch, CURLOPT_FAILONERROR, false); $nobody = !$use_get; //Whether to send a HEAD request (the default) or a GET request $parts = @parse_url($url); if ($parts['scheme'] == 'https') { curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //Required to make HTTPS URLs work. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $nobody = false; //Can't use HEAD with HTTPS. } if ($nobody) { //If possible, use HEAD requests for speed. curl_setopt($ch, CURLOPT_NOBODY, true); } else { //If we must use GET at least limit the amount of downloaded data. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Range: bytes=0-2048')); //2 KB } //Register a callback function which will process the HTTP header(s). //It can be called multiple times if the remote server performs a redirect. curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$this, 'read_header')); //Execute the request $start_time = microtime_float(); curl_exec($ch); $measured_request_duration = microtime_float() - $start_time; $info = curl_getinfo($ch); //Store the results $result['http_code'] = intval($info['http_code']); $result['final_url'] = $info['url']; $result['request_duration'] = $info['total_time']; $result['redirect_count'] = $info['redirect_count']; //CURL doesn't return a request duration when a timeout happens, so we measure it ourselves. //It is useful to see how long the plugin waited for the server to respond before assuming it timed out. if (empty($result['request_duration'])) { $result['request_duration'] = $measured_request_duration; } //Determine if the link counts as "broken" if ($result['http_code'] == 0) { $result['broken'] = true; $error_code = curl_errno($ch); $log .= sprintf("%s [Error #%d]\n", curl_error($ch), $error_code); //We only handle a couple of CURL error codes; most are highly esoteric. //libcurl "CURLE_" constants can't be used here because some of them have //different names or values in PHP. switch ($error_code) { case 6: //CURLE_COULDNT_RESOLVE_HOST $result['status_code'] = BLC_LINK_STATUS_WARNING; $result['status_text'] = __('Server Not Found', 'broken-link-checker'); break; case 28: //CURLE_OPERATION_TIMEDOUT $result['timeout'] = true; break; case 7: //CURLE_COULDNT_CONNECT //More often than not, this error code indicates that the connection attempt //timed out. This heuristic tries to distinguish between connections that fail //due to timeouts and those that fail due to other causes. if ($result['request_duration'] >= 0.9 * $conf->options['timeout']) { $result['timeout'] = true; } else { $result['status_code'] = BLC_LINK_STATUS_WARNING; $result['status_text'] = __('Connection Failed', 'broken-link-checker'); } break; default: $result['status_code'] = BLC_LINK_STATUS_WARNING; $result['status_text'] = __('Unknown Error', 'broken-link-checker'); } } else { $result['broken'] = $this->is_error_code($result['http_code']); } curl_close($ch); if ($nobody && $result['broken']) { //The site in question might be expecting GET instead of HEAD, so lets retry the request //using the GET verb. return $this->check($url, true); //Note : normally a server that doesn't allow HEAD requests on a specific resource *should* //return "405 Method Not Allowed". Unfortunately, there are sites that return 404 or //another, even more general, error code instead. So just checking for 405 wouldn't be enough. } //When safe_mode or open_basedir is enabled CURL will be forbidden from following redirects, //so redirect_count will be 0 for all URLs. As a workaround, set it to 1 when the HTTP //response codes indicates a redirect but redirect_count is zero. //Note to self : Extracting the Location header might also be helpful. if ($result['redirect_count'] == 0 && in_array($result['http_code'], array(301, 302, 303, 307))) { $result['redirect_count'] = 1; } //Build the log from HTTP code and headers. $log .= '=== '; if ($result['http_code']) { $log .= sprintf(__('HTTP code : %d', 'broken-link-checker'), $result['http_code']); } else { $log .= __('(No response)', 'broken-link-checker'); } $log .= " ===\n\n"; $log .= $this->last_headers; if (!empty($result['broken']) && !empty($result['timeout'])) { $log .= "\n(" . __("Most likely the connection timed out or the domain doesn't exist.", 'broken-link-checker') . ')'; } $result['log'] = $log; //The hash should contain info about all pieces of data that pertain to determining if the //link is working. $result['result_hash'] = implode('|', array($result['http_code'], !empty($result['broken']) ? 'broken' : '0', !empty($result['timeout']) ? 'timeout' : '0', md5($result['final_url']))); return $result; }
/** * Collect various debugging information and return it in an associative array * * @return array */ function get_debug_info() { global $wpdb; //Collect some information that's useful for debugging $debug = array(); //PHP version. Any one is fine as long as WP supports it. $debug[__('PHP version', 'broken-link-checker')] = array('state' => 'ok', 'value' => phpversion()); //MySQL version $debug[__('MySQL version', 'broken-link-checker')] = array('state' => 'ok', 'value' => @mysql_get_server_info($wpdb->dbh)); //CURL presence and version if (function_exists('curl_version')) { $version = curl_version(); if (version_compare($version['version'], '7.16.0', '<=')) { $data = array('state' => 'warning', 'value' => $version['version'], 'message' => __('You have an old version of CURL. Redirect detection may not work properly.', 'broken-link-checker')); } else { $data = array('state' => 'ok', 'value' => $version['version']); } } else { $data = array('state' => 'warning', 'value' => __('Not installed', 'broken-link-checker')); } $debug[__('CURL version', 'broken-link-checker')] = $data; //Snoopy presence if (class_exists('Snoopy') || file_exists(ABSPATH . WPINC . '/class-snoopy.php')) { $data = array('state' => 'ok', 'value' => __('Installed', 'broken-link-checker')); } else { //No Snoopy? This should never happen, but if it does we *must* have CURL. if (function_exists('curl_init')) { $data = array('state' => 'ok', 'value' => __('Not installed', 'broken-link-checker')); } else { $data = array('state' => 'error', 'value' => __('Not installed', 'broken-link-checker'), 'message' => __('You must have either CURL or Snoopy installed for the plugin to work!', 'broken-link-checker')); } } $debug['Snoopy'] = $data; //Safe_mode status if (blcUtility::is_safe_mode()) { $debug['Safe mode'] = array('state' => 'warning', 'value' => __('On', 'broken-link-checker'), 'message' => __('Redirects may be detected as broken links when safe_mode is on.', 'broken-link-checker')); } else { $debug['Safe mode'] = array('state' => 'ok', 'value' => __('Off', 'broken-link-checker')); } //Open_basedir status if (blcUtility::is_open_basedir()) { $debug['open_basedir'] = array('state' => 'warning', 'value' => sprintf(__('On ( %s )', 'broken-link-checker'), ini_get('open_basedir')), 'message' => __('Redirects may be detected as broken links when open_basedir is on.', 'broken-link-checker')); } else { $debug['open_basedir'] = array('state' => 'ok', 'value' => __('Off', 'broken-link-checker')); } //Lockfile location $lockfile = $this->lockfile_name(); if ($lockfile) { $debug['Lockfile'] = array('state' => 'ok', 'value' => $lockfile); } else { $debug['Lockfile'] = array('state' => 'error', 'message' => __("Can't create a lockfile. Please specify a custom temporary directory.", 'broken-link-checker')); } return $debug; }