✅ Fixed bulk conversion getting stuck on missing files ✅ Added robust error handling and timeout protection ✅ Improved JavaScript response parsing ✅ Added file existence validation ✅ Fixed missing PHP class imports ✅ Added comprehensive try-catch error recovery 🔧 Key fixes: - File existence checks before conversion attempts - 30-second timeout protection per file - Graceful handling of 500 errors and JSON parsing issues - Automatic continuation to next file on failures - Cache busting for JavaScript updates 🎯 Result: Bulk conversion now completes successfully even with missing files 🚀 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
793 lines
38 KiB
PHP
793 lines
38 KiB
PHP
<?php
|
|
|
|
namespace WebPExpress;
|
|
|
|
use \WebPExpress\Paths;
|
|
|
|
class SelfTestHelper
|
|
{
|
|
|
|
public static function deleteFilesInDir($dir, $filePattern = "*")
|
|
{
|
|
foreach (glob($dir . DIRECTORY_SEPARATOR . $filePattern) as $filename) {
|
|
unlink($filename);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove files in dir and the dir. Does not remove files recursively.
|
|
*/
|
|
public static function deleteDir($dir)
|
|
{
|
|
if (@file_exists($dir)) {
|
|
self::deleteFilesInDir($dir);
|
|
rmdir($dir);
|
|
}
|
|
}
|
|
|
|
|
|
public static function deleteTestImagesInFolder($rootId)
|
|
{
|
|
$testDir = Paths::getAbsDirById($rootId) . '/webp-express-test-images';
|
|
self::deleteDir($testDir);
|
|
}
|
|
|
|
public static function cleanUpTestImages($rootId, $config)
|
|
{
|
|
// Clean up test images in source folder
|
|
self::deleteTestImagesInFolder($rootId);
|
|
|
|
// Clean up dummy webp images in cache folder for the root
|
|
$cacheDirForRoot = Paths::getCacheDirForImageRoot(
|
|
$config['destination-folder'],
|
|
$config['destination-structure'],
|
|
$rootId
|
|
);
|
|
|
|
$testDir = $cacheDirForRoot . '/webp-express-test-images';
|
|
self::deleteDir($testDir);
|
|
}
|
|
|
|
public static function copyFile($source, $destination)
|
|
{
|
|
$log = [];
|
|
if (@copy($source, $destination)) {
|
|
return [true, $log];
|
|
} else {
|
|
$log[] = 'Failed to copy *' . $source . '* to *' . $destination . '*';
|
|
if (!@file_exists($source)) {
|
|
$log[] = 'The source file was not found';
|
|
} else {
|
|
if (!@file_exists(dirname($destination))) {
|
|
$log[] = 'The destination folder does not exist!';
|
|
} else {
|
|
$log[] = 'This is probably a permission issue. Check that your webserver has permission to ' .
|
|
'write files in the directory (*' . dirname($destination) . '*)';
|
|
}
|
|
}
|
|
return [false, $log];
|
|
}
|
|
}
|
|
|
|
public static function randomDigitsAndLetters($length)
|
|
{
|
|
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
$charactersLength = strlen($characters);
|
|
$randomString = '';
|
|
for ($i = 0; $i < $length; $i++) {
|
|
$randomString .= $characters[rand(0, $charactersLength - 1)];
|
|
}
|
|
return $randomString;
|
|
}
|
|
|
|
public static function copyTestImageToRoot($rootId, $imageType = 'jpeg')
|
|
{
|
|
// TODO: Copy to a subfolder instead
|
|
// TODO: Use smaller jpeg / pngs please.
|
|
$log = [];
|
|
switch ($imageType) {
|
|
case 'jpeg':
|
|
$fileNameToCopy = 'very-small.jpg';
|
|
break;
|
|
case 'png':
|
|
$fileNameToCopy = 'test.png';
|
|
break;
|
|
}
|
|
$testSource = Paths::getPluginDirAbs() . '/webp-express/test/' . $fileNameToCopy;
|
|
$filenameOfDestination = self::randomDigitsAndLetters(6) . '.' . strtoupper($imageType);
|
|
//$filenameOfDestination = self::randomDigitsAndLetters(6) . '.' . $imageType;
|
|
$log[] = 'Copying ' . strtoupper($imageType) . ' to ' . $rootId . ' folder (*webp-express-test-images/' . $filenameOfDestination . '*)';
|
|
|
|
$destDir = Paths::getAbsDirById($rootId) . '/webp-express-test-images';
|
|
$destination = $destDir . '/' . $filenameOfDestination;
|
|
|
|
if (!@file_exists($destDir)) {
|
|
if (!@mkdir($destDir)) {
|
|
$log[count($log) - 1] .= '. FAILED';
|
|
$log[] = 'Failed to create folder for test images: ' . $destDir;
|
|
return [$log, false, ''];
|
|
}
|
|
}
|
|
|
|
list($success, $errors) = self::copyFile($testSource, $destination);
|
|
if (!$success) {
|
|
$log[count($log) - 1] .= '. FAILED';
|
|
$log = array_merge($log, $errors);
|
|
return [$log, false, ''];
|
|
} else {
|
|
$log[count($log) - 1] .= '. ok!';
|
|
$log[] = 'We now have a ' . $imageType . ' stored here:';
|
|
$log[] = '*' . $destination . '*';
|
|
}
|
|
return [$log, true, $filenameOfDestination];
|
|
}
|
|
|
|
public static function copyTestImageToUploadFolder($imageType = 'jpeg')
|
|
{
|
|
return self::copyTestImageToRoot('uploads', $imageType);
|
|
}
|
|
|
|
public static function copyDummyWebPToCacheFolder($rootId, $destinationFolder, $destinationExtension, $destinationStructure, $sourceFileName, $imageType = 'jpeg')
|
|
{
|
|
$log = [];
|
|
$dummyWebP = Paths::getPluginDirAbs() . '/webp-express/test/test.jpg.webp';
|
|
|
|
$log[] = 'Copying dummy webp to the cache root for ' . $rootId;
|
|
$destDir = Paths::getCacheDirForImageRoot($destinationFolder, $destinationStructure, $rootId);
|
|
if (!file_exists($destDir)) {
|
|
$log[] = 'The folder did not exist. Creating folder at: ' . $destinationFolder;
|
|
if (!mkdir($destDir, 0777, true)) {
|
|
$log[] = 'Failed creating folder!';
|
|
return [$log, false, ''];
|
|
}
|
|
}
|
|
$destDir .= '/webp-express-test-images';
|
|
if (!file_exists($destDir)) {
|
|
if (!mkdir($destDir, 0755, false)) {
|
|
$log[] = 'Failed creating the folder for the test images:';
|
|
$log[] = $destDir;
|
|
$log[] = 'To run this test, you must grant write permissions';
|
|
return [$log, false, ''];
|
|
}
|
|
}
|
|
|
|
$filenameOfDestination = ConvertHelperIndependent::appendOrSetExtension(
|
|
$sourceFileName,
|
|
$destinationFolder,
|
|
$destinationExtension,
|
|
($rootId == 'uploads')
|
|
);
|
|
|
|
//$filenameOfDestination = $destinationFileNameNoExt . ($destinationExtension == 'append' ? '.' . $imageType : '') . '.webp';
|
|
$destination = $destDir . '/' . $filenameOfDestination;
|
|
|
|
list($success, $errors) = self::copyFile($dummyWebP, $destination);
|
|
if (!$success) {
|
|
$log[count($log) - 1] .= '. FAILED';
|
|
$log = array_merge($log, $errors);
|
|
return [$log, false, ''];
|
|
} else {
|
|
$log[count($log) - 1] .= '. ok!';
|
|
$log[] = 'We now have a webp file stored here:';
|
|
$log[] = '*' . $destination . '*';
|
|
$log[] = '';
|
|
}
|
|
return [$log, true, $destination];
|
|
}
|
|
|
|
/**
|
|
* Perform HTTP request.
|
|
*
|
|
* @param string $requestUrl URL
|
|
* @param array $args Args to pass to wp_remote_get. Note however that "redirection" is set to 0
|
|
* @param int $maxRedirects For internal use
|
|
* @return array The result
|
|
* $success (boolean): If we got a 200 response in the end (after max 2 redirects)
|
|
* $log (array) : Message log
|
|
* $results : Array of results from wp_remote_get. If no redirection occured, it will only contain one item.
|
|
*
|
|
*/
|
|
public static function remoteGet($requestUrl, $args = [], $maxRedirects = 2)
|
|
{
|
|
$log = [];
|
|
$args['redirection'] = 0;
|
|
|
|
if (defined('WP_DEBUG') && WP_DEBUG ) {
|
|
// Prevent errors with unverified certificates (#379)
|
|
$args['sslverify'] = false;
|
|
}
|
|
|
|
$log[] = 'Request URL: ' . $requestUrl;
|
|
|
|
$results = [];
|
|
$wpResult = wp_remote_get($requestUrl, $args);
|
|
if (is_wp_error($wpResult)) {
|
|
$log[] = 'The remote request errored';
|
|
$log[] = $wpResult->get_error_message();
|
|
//$log[] = print_r($wpResult, true);
|
|
return [false, $log, $results];
|
|
}
|
|
if (!is_wp_error($wpResult) && !isset($wpResult['headers'])) {
|
|
$wpResult['headers'] = [];
|
|
}
|
|
$results[] = $wpResult;
|
|
$responseCode = $wpResult['response']['code'];
|
|
|
|
$log[] = 'Response: ' . $responseCode . ' ' . $wpResult['response']['message'];
|
|
$log = array_merge($log, SelfTestHelper::printHeaders($wpResult['headers']));
|
|
|
|
if (isset($wpResult['headers']['content-type'])) {
|
|
if (strpos($wpResult['headers']['content-type'], 'text/html') !== false) {
|
|
if (isset($wpResult['body']) && (!empty($wpResult['body']))) {
|
|
$log[] = 'Body:';
|
|
$log[] = print_r($wpResult['body'], true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (($responseCode == '302') || ($responseCode == '301')) {
|
|
if ($maxRedirects > 0) {
|
|
if (isset($wpResult['headers']['location'])) {
|
|
$url = $wpResult['headers']['location'];
|
|
if (strpos($url, 'http') !== 0) {
|
|
$url = $requestUrl . $url;
|
|
}
|
|
$log[] = 'Following that redirect';
|
|
|
|
list($success, $newLog, $newResult) = self::remoteGet($url, $args, $maxRedirects - 1);
|
|
$log = array_merge($log, $newLog);
|
|
$results = array_merge($results, $newResult);
|
|
|
|
return [$success, $log, $results];
|
|
|
|
}
|
|
} else {
|
|
$log[] = 'Not following the redirect (max redirects exceeded)';
|
|
}
|
|
}
|
|
|
|
$success = ($responseCode == '200');
|
|
return [$success, $log, $results];
|
|
}
|
|
|
|
public static function hasHeaderContaining($headers, $headerToInspect, $containString)
|
|
{
|
|
if (!isset($headers[$headerToInspect])) {
|
|
return false;
|
|
}
|
|
|
|
// If there are multiple headers, check all
|
|
if (gettype($headers[$headerToInspect]) == 'string') {
|
|
$h = [$headers[$headerToInspect]];
|
|
} else {
|
|
$h = $headers[$headerToInspect];
|
|
}
|
|
foreach ($h as $headerValue) {
|
|
if (stripos($headerValue, $containString) !== false) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static function hasVaryAcceptHeader($headers)
|
|
{
|
|
if (!isset($headers['vary'])) {
|
|
return false;
|
|
}
|
|
|
|
// There may be multiple Vary headers. Or they might be combined in one.
|
|
// Both are acceptable, according to https://stackoverflow.com/a/28799169/842756
|
|
if (gettype($headers['vary']) == 'string') {
|
|
$varyHeaders = [$headers['vary']];
|
|
} else {
|
|
$varyHeaders = $headers['vary'];
|
|
}
|
|
foreach ($varyHeaders as $headerValue) {
|
|
$values = explode(',', $headerValue);
|
|
foreach ($values as $value) {
|
|
if (strtolower($value) == 'accept') {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param string $rule existing|webp-on-demand|webp-realizer
|
|
*/
|
|
public static function diagnoseNoVaryHeader($rootId, $rule)
|
|
{
|
|
$log = [];
|
|
$log[] = '**However, we did not receive a Vary:Accept header. ' .
|
|
'That header should be set in order to tell proxies that the response varies depending on the ' .
|
|
'Accept header. Otherwise browsers not supporting webp might get a cached webp and vice versa.**{: .warn}';
|
|
|
|
$log[] = 'Too technical? ';
|
|
$log[] = 'Here is an explanation of what this means: ' .
|
|
'Some companies have set up proxies which caches resources. This way, if employee A have downloaded an ' .
|
|
'image and employee B requests it, the proxy can deliver the image directly to employee B without needing to ' .
|
|
'send a request to the server. ' .
|
|
'This is clever, but it can go wrong. If B for some reason is meant to get another image than A, it will not ' .
|
|
'happen, as the server does not get the request. That is where the Vary header comes in. It tells the proxy ' .
|
|
'that the image is dependent upon something. In this case, we need to signal proxies that the image depends upon ' .
|
|
'the "Accept" header, as this is the one browsers use to tell the server if it accepts webps or not. ' .
|
|
'We do that using the "Vary:Accept" header. However - it is missing :( ' .
|
|
'Which means that employees at (larger) companies might experience problems if some are using browsers ' .
|
|
'that supports webp and others are using browsers that does not. Worst case is that the request to an image ' .
|
|
'is done with a browser that supports webp, as this will cache the webp in the proxy, and deliver webps to ' .
|
|
'all employees - even to those who uses browsers that does not support webp. These employees will get blank images.';
|
|
|
|
if ($rule == 'existing') {
|
|
$log[] = 'So, what should you do? **I would recommend that you either try to fix the problem with the missing Vary:Accept ' .
|
|
'header or change to "CDN friendly" mode.**{: .warn}';
|
|
} elseif ($rule == 'webp-on-demand') {
|
|
$log[] = 'So, what should you do? **I would recommend that you either try to fix the problem with the missing Vary:Accept ' .
|
|
'header or disable the "Enable redirection to converter?" option and use another way to get the images converted - ie ' .
|
|
'Bulk Convert or Convert on Upload**{: .warn}';
|
|
}
|
|
|
|
|
|
|
|
return $log;
|
|
}
|
|
|
|
public static function hasCacheControlOrExpiresHeader($headers)
|
|
{
|
|
if (isset($headers['cache-control'])) {
|
|
return true;
|
|
}
|
|
if (isset($headers['expires'])) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
public static function flattenHeaders($headers)
|
|
{
|
|
$log = [];
|
|
foreach ($headers as $headerName => $headerValue) {
|
|
if (gettype($headerValue) == 'array') {
|
|
foreach ($headerValue as $i => $value) {
|
|
$log[] = [$headerName, $value];
|
|
}
|
|
} else {
|
|
$log[] = [$headerName, $headerValue];
|
|
}
|
|
}
|
|
return $log;
|
|
}
|
|
|
|
public static function printHeaders($headers)
|
|
{
|
|
$log = [];
|
|
$log[] = '#### Response headers:';
|
|
|
|
$headersFlat = self::flattenHeaders($headers);
|
|
//
|
|
foreach ($headersFlat as $i => list($headerName, $headerValue)) {
|
|
if ($headerName == 'x-webp-express-error') {
|
|
$headerValue = '**' . $headerValue . '**{: .error}';
|
|
}
|
|
$log[] = '- ' . $headerName . ': ' . $headerValue;
|
|
}
|
|
$log[] = '';
|
|
return $log;
|
|
}
|
|
|
|
private static function trueFalseNullString($var)
|
|
{
|
|
if ($var === true) {
|
|
return 'yes';
|
|
}
|
|
if ($var === false) {
|
|
return 'no';
|
|
}
|
|
return 'could not be determined';
|
|
}
|
|
|
|
public static function systemInfo()
|
|
{
|
|
$log = [];
|
|
$log[] = '#### System info:';
|
|
$log[] = '- PHP version: ' . phpversion();
|
|
$log[] = '- OS: ' . PHP_OS;
|
|
$log[] = '- Server software: ' . $_SERVER["SERVER_SOFTWARE"];
|
|
$log[] = '- Document Root status: ' . Paths::docRootStatusText();
|
|
if (PathHelper::isDocRootAvailable()) {
|
|
$log[] = '- Document Root: ' . $_SERVER['DOCUMENT_ROOT'];
|
|
}
|
|
if (PathHelper::isDocRootAvailableAndResolvable()) {
|
|
if ($_SERVER['DOCUMENT_ROOT'] != realpath($_SERVER['DOCUMENT_ROOT'])) {
|
|
$log[] = '- Document Root (symlinked resolved): ' . realpath($_SERVER['DOCUMENT_ROOT']);
|
|
}
|
|
}
|
|
|
|
$log[] = '- Document Root: ' . Paths::docRootStatusText();
|
|
$log[] = '- Apache module "mod_rewrite" enabled?: ' . self::trueFalseNullString(PlatformInfo::gotApacheModule('mod_rewrite'));
|
|
$log[] = '- Apache module "mod_headers" enabled?: ' . self::trueFalseNullString(PlatformInfo::gotApacheModule('mod_headers'));
|
|
return $log;
|
|
}
|
|
|
|
public static function wordpressInfo()
|
|
{
|
|
$log = [];
|
|
$log[] = '#### Wordpress info:';
|
|
$log[] = '- Version: ' . get_bloginfo('version');
|
|
$log[] = '- Multisite?: ' . self::trueFalseNullString(is_multisite());
|
|
$log[] = '- Is wp-content moved?: ' . self::trueFalseNullString(Paths::isWPContentDirMoved());
|
|
$log[] = '- Is uploads moved out of wp-content?: ' . self::trueFalseNullString(Paths::isUploadDirMovedOutOfWPContentDir());
|
|
$log[] = '- Is plugins moved out of wp-content?: ' . self::trueFalseNullString(Paths::isPluginDirMovedOutOfWpContent());
|
|
|
|
$log[] = '';
|
|
|
|
$log[] = '#### Image roots (absolute paths)';
|
|
foreach (Paths::getImageRootIds() as $rootId) {
|
|
$absDir = Paths::getAbsDirById($rootId);
|
|
|
|
if (PathHelper::pathExistsAndIsResolvable($absDir) && ($absDir != realpath($absDir))) {
|
|
$log[] = '*' . $rootId . '*: ' . $absDir . ' (resolved for symlinks: ' . realpath($absDir) . ')';
|
|
} else {
|
|
$log[] = '*' . $rootId . '*: ' . $absDir;
|
|
|
|
}
|
|
}
|
|
|
|
$log[] = '#### Image roots (relative to document root)';
|
|
foreach (Paths::getImageRootIds() as $rootId) {
|
|
$absPath = Paths::getAbsDirById($rootId);
|
|
if (PathHelper::canCalculateRelPathFromDocRootToDir($absPath)) {
|
|
$log[] = '*' . $rootId . '*: ' . PathHelper::getRelPathFromDocRootToDirNoDirectoryTraversalAllowed($absPath);
|
|
} else {
|
|
$log[] = '*' . $rootId . '*: ' . 'n/a (not within document root)';
|
|
}
|
|
}
|
|
|
|
$log[] = '#### Image roots (URLs)';
|
|
foreach (Paths::getImageRootIds() as $rootId) {
|
|
$url = Paths::getUrlById($rootId);
|
|
$log[] = '*' . $rootId . '*: ' . $url;
|
|
}
|
|
|
|
|
|
return $log;
|
|
}
|
|
|
|
public static function configInfo($config)
|
|
{
|
|
$log = [];
|
|
$log[] = '#### WebP Express configuration info:';
|
|
$log[] = '- Destination folder: ' . $config['destination-folder'];
|
|
$log[] = '- Destination extension: ' . $config['destination-extension'];
|
|
$log[] = '- Destination structure: ' . $config['destination-structure'];
|
|
//$log[] = 'Image types: ' . ;
|
|
//$log[] = '';
|
|
$log[] = '(To view all configuration, take a look at the config file, which is stored in *' . Paths::getConfigFileName() . '*)';
|
|
//$log[] = '- Config file: (config.json)';
|
|
//$log[] = "'''\n" . json_encode($config, JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT) . "\n'''\n";
|
|
return $log;
|
|
}
|
|
|
|
public static function htaccessInfo($config, $printRules = true)
|
|
{
|
|
$log = [];
|
|
//$log[] = '*.htaccess info:*';
|
|
//$log[] = '- Image roots with WebP Express rules: ' . implode(', ', HTAccess::getRootsWithWebPExpressRulesIn());
|
|
$log[] = '#### .htaccess files that WebP Express have placed rules in the following files:';
|
|
$rootIds = HTAccess::getRootsWithWebPExpressRulesIn();
|
|
foreach ($rootIds as $imageRootId) {
|
|
$log[] = '- ' . Paths::getAbsDirById($imageRootId) . '/.htaccess';
|
|
}
|
|
|
|
foreach ($rootIds as $imageRootId) {
|
|
$log = array_merge($log, self::rulesInImageRoot($config, $imageRootId));
|
|
}
|
|
|
|
return $log;
|
|
}
|
|
|
|
public static function rulesInImageRoot($config, $imageRootId)
|
|
{
|
|
$log = [];
|
|
$file = Paths::getAbsDirById($imageRootId) . '/.htaccess';
|
|
$log[] = '#### WebP rules in *' .
|
|
($imageRootId == 'cache' ? 'webp image cache' : $imageRootId) . '*:';
|
|
$log[] = 'File: ' . $file;
|
|
if (!HTAccess::haveWeRulesInThisHTAccess($file)) {
|
|
$log[] = '**NONE!**{: .warn}';
|
|
} else {
|
|
$weRules = HTAccess::extractWebPExpressRulesFromHTAccess($file);
|
|
// remove unindented comments
|
|
//$weRules = preg_replace('/^\#\s[^\n\r]*[\n\r]+/ms', '', $weRules);
|
|
|
|
// remove comments in the beginning
|
|
$weRulesArr = preg_split("/\r\n|\n|\r/", $weRules); // https://stackoverflow.com/a/11165332/842756
|
|
while ((strlen($weRulesArr[0]) > 0) && ($weRulesArr[0][0] == '#')) {
|
|
array_shift($weRulesArr);
|
|
}
|
|
$weRules = implode("\n", $weRulesArr);
|
|
|
|
$log[] = '```' . $weRules . '```';
|
|
}
|
|
return $log;
|
|
}
|
|
|
|
public static function rulesInUpload($config)
|
|
{
|
|
return self::rulesInImageRoot($config, 'uploads');
|
|
}
|
|
|
|
public static function allInfo($config)
|
|
{
|
|
$log = [];
|
|
|
|
$log = array_merge($log, self::systemInfo());
|
|
$log = array_merge($log, self::wordpressInfo());
|
|
$log = array_merge($log, self::configInfo($config));
|
|
$log = array_merge($log, self::capabilityTests($config));
|
|
$log = array_merge($log, self::htaccessInfo($config, true));
|
|
//$log = array_merge($log, self::rulesInImageRoot($config, 'upload'));
|
|
//$log = array_merge($log, self::rulesInImageRoot($config, 'wp-content'));
|
|
return $log;
|
|
}
|
|
|
|
public static function capabilityTests($config)
|
|
{
|
|
$capTests = $config['base-htaccess-on-these-capability-tests'];
|
|
$log = [];
|
|
$log[] = '#### Live tests of .htaccess capabilities / system configuration:';
|
|
$log[] = 'Unless noted otherwise, the tests are run in *wp-content/webp-express/htaccess-capability-tester*. ';
|
|
$log[] = 'WebPExpress currently treats the results as they neccessarily applies to all scopes (upload, themes, etc), ';
|
|
$log[] = 'but note that a server might be configured to have mod_rewrite disallowed in some folders and allowed in others.';
|
|
/*$log[] = 'Exactly what you can do in a *.htaccess* depends on the server setup. WebP Express ' .
|
|
'makes some live tests to verify if a certain feature in fact works. This is done by creating ' .
|
|
'test files (*.htaccess* files and php files) in a dir inside the content dir and running these. ' .
|
|
'These test results are used when creating the rewrite rules. Here are the results:';*/
|
|
|
|
// $log[] = '';
|
|
$log[] = '- .htaccess files enabled?: ' . self::trueFalseNullString(HTAccessCapabilityTestRunner::htaccessEnabled());
|
|
$log[] = '- mod_rewrite working?: ' . self::trueFalseNullString(HTAccessCapabilityTestRunner::modRewriteWorking());
|
|
$log[] = '- mod_headers loaded?: ' . self::trueFalseNullString(HTAccessCapabilityTestRunner::modHeadersLoaded());
|
|
$log[] = '- mod_headers working (header set): ' . self::trueFalseNullString(HTAccessCapabilityTestRunner::modHeaderWorking());
|
|
//$log[] = '- passing variables from *.htaccess* to PHP script through environment variable working?: ' . self::trueFalseNullString($capTests['passThroughEnvWorking']);
|
|
$log[] = '- passing variables from *.htaccess* to PHP script through environment variable working?: ' . self::trueFalseNullString(HTAccessCapabilityTestRunner::passThroughEnvWorking());
|
|
$log[] = '- Can run php test file in plugins/webp-express/wod/ ?: ' . self::trueFalseNullString(HTAccessCapabilityTestRunner::canRunTestScriptInWOD());
|
|
$log[] = '- Can run php test file in plugins/webp-express/wod2/ ?: ' . self::trueFalseNullString(HTAccessCapabilityTestRunner::canRunTestScriptInWOD2());
|
|
$log[] = '- Directives for granting access like its done in wod/.htaccess allowed?: ' . self::trueFalseNullString(HTAccessCapabilityTestRunner::grantAllAllowed());
|
|
/*$log[] = '- pass variable from *.htaccess* to script through header working?: ' .
|
|
self::trueFalseNullString($capTests['passThroughHeaderWorking']);*/
|
|
return $log;
|
|
}
|
|
|
|
public static function diagnoseFailedRewrite($config, $headers)
|
|
{
|
|
if (($config['destination-structure'] == 'image-roots') && (!PathHelper::isDocRootAvailableAndResolvable())) {
|
|
$log[] = 'The problem is probably this combination:';
|
|
if (!PathHelper::isDocRootAvailable()) {
|
|
$log[] = '1. Your document root isn`t available';
|
|
} else {
|
|
$log[] = '1. Your document root isn`t resolvable for symlinks (it is probably subject to open_basedir restriction)';
|
|
}
|
|
$log[] = '2. Your document root is symlinked';
|
|
$log[] = '3. The wordpress function that tells the path of the uploads folder returns the symlink resolved path';
|
|
|
|
$log[] = 'I cannot check if your document root is in fact symlinked (as document root isnt resolvable). ' .
|
|
'But if it is, there you have it. The line beginning with "RewriteCond %{REQUEST_FILENAME}"" points to your resolved root, ' .
|
|
'but it should point to your symlinked root. WebP Express cannot do that for you because it cannot discover what the symlink is. ' .
|
|
'Try changing the line manually. When it works, you can move the rules outside the WebP Express block so they dont get ' .
|
|
'overwritten. OR you can change your server configuration (document root / open_basedir restrictions)';
|
|
}
|
|
|
|
//$log[] = '## Diagnosing';
|
|
|
|
//if (PlatformInfo::isNginx()) {
|
|
if (strpos($headers['server'], 'nginx') === 0) {
|
|
|
|
// Nginx
|
|
$log[] = 'Notice that you are on Nginx and the rules that WebP Express stores in the *.htaccess* files probably does not ' .
|
|
'have any effect. ';
|
|
$log[] = 'Please read the "I am on Nginx" section in the FAQ (https://wordpress.org/plugins/webp-express/)';
|
|
$log[] = 'And did you remember to restart the nginx service after updating the configuration?';
|
|
|
|
$log[] = 'PS: If you cannot get the redirect to work, you can simply rely on Alter HTML as described in the FAQ.';
|
|
return $log;
|
|
}
|
|
|
|
$modRewriteWorking = HTAccessCapabilityTestRunner::modRewriteWorking();
|
|
if ($modRewriteWorking !== null) {
|
|
$log[] = 'Running a special designed capability test to test if rewriting works with *.htaccess* files';
|
|
}
|
|
if ($modRewriteWorking === true) {
|
|
$log[] = 'Result: Yes, rewriting works.';
|
|
$log[] = 'It seems something is wrong with the *.htaccess* rules then. You could try ' .
|
|
'to change "Destination structure" - the rules there are quite different.';
|
|
$log[] = 'It could also be that the server has cached the configuration a while. Some servers ' .
|
|
'does that. In that case, simply give it a few minutes and try again.';
|
|
} elseif ($modRewriteWorking === false) {
|
|
$log[] = 'Result: No, rewriting does not seem to work within *.htaccess* rules.';
|
|
if (PlatformInfo::definitelyNotGotModRewrite()) {
|
|
$log[] = 'It actually seems "mod_write" is disabled on your server. ' .
|
|
'**You must enable mod_rewrite on the server**';
|
|
} elseif (PlatformInfo::definitelyGotApacheModule('mod_rewrite')) {
|
|
$log[] = 'However, "mod_write" *is* enabled on your server. This seems to indicate that ' .
|
|
'*.htaccess* files has been disabled for configuration on your server. ' .
|
|
'In that case, you need to copy the WebP Express rules from the *.htaccess* files into your virtual host configuration files. ' .
|
|
'(WebP Express generates multiple *.htaccess* files. Look in the upload folder, the wp-content folder, etc).';
|
|
$log[] = 'It could however alse simply be that your server simply needs some time. ' .
|
|
'Some servers caches the *.htaccess* rules for a bit. In that case, simply give it a few minutes and try again.';
|
|
} else {
|
|
$log[] = 'However, this could be due to your server being a bit slow on picking up changes in *.htaccess*.' .
|
|
'Give it a few minutes and try again.';
|
|
}
|
|
} else {
|
|
// The mod_rewrite test could not conclude anything.
|
|
if (PlatformInfo::definitelyNotGotApacheModule('mod_rewrite')) {
|
|
$log[] = 'It actually seems "mod_write" is disabled on your server. ' .
|
|
'**You must enable mod_rewrite on the server**';
|
|
} elseif (PlatformInfo::definitelyGotApacheModule('mod_rewrite')) {
|
|
$log[] = '"mod_write" is enabled on your server, so rewriting ought to work. ' .
|
|
'However, it could be that your server setup has disabled *.htaccess* files for configuration. ' .
|
|
'In that case, you need to copy the WebP Express rules from the *.htaccess* files into your virtual host configuration files. ' .
|
|
'(WebP Express generates multiple *.htaccess* files. Look in the upload folder, the wp-content folder, etc). ';
|
|
} else {
|
|
$log[] = 'It seems something is wrong with the *.htaccess* rules. ';
|
|
$log[] = 'Or perhaps the server has cached the configuration a while. Some servers ' .
|
|
'does that. In that case, simply give it a few minutes and try again.';
|
|
}
|
|
}
|
|
$log[] = 'Note that if you cannot get redirection to work, you can switch to "CDN friendly" mode and ' .
|
|
'rely on the "Alter HTML" functionality to point to the webp images. If you do a bulk conversion ' .
|
|
'and make sure that "Convert upon upload" is activated, you should be all set. Alter HTML even handles ' .
|
|
'inline css (unless you select "picture tag" syntax). It does however not handle images in external css or ' .
|
|
'which is added dynamically with javascript.';
|
|
|
|
$log[] = '## Info for manually diagnosing';
|
|
$log = array_merge($log, self::allInfo($config));
|
|
return $log;
|
|
}
|
|
|
|
public static function diagnoseWod403or500($config, $rootId, $responseCode)
|
|
{
|
|
$log = [];
|
|
|
|
$htaccessRules = SelfTestHelper::rulesInImageRoot($config, $rootId);
|
|
$rulesText = implode('', $htaccessRules);
|
|
$rulesPointsToWod = (strpos($rulesText, '/wod/') > 0);
|
|
$rulesPointsToWod2 = (strpos($rulesText, '/wod2/') !== false);
|
|
|
|
$log[] = '';
|
|
$log[] = '**diagnosing**';
|
|
$canRunTestScriptInWod = HTAccessCapabilityTestRunner::canRunTestScriptInWOD();
|
|
$canRunTestScriptInWod2 = HTAccessCapabilityTestRunner::canRunTestScriptInWOD2();
|
|
$canRunInAnyWod = ($canRunTestScriptInWod || $canRunTestScriptInWod2);
|
|
|
|
$responsePingPhp = wp_remote_get(Paths::getPluginsUrl() . '/webp-express/wod/ping.php', ['timeout' => 7]);
|
|
$pingPhpResponseCode = wp_remote_retrieve_response_code($responsePingPhp);
|
|
|
|
$responsePingText = wp_remote_get(Paths::getPluginsUrl() . '/webp-express/wod/ping.txt', ['timeout' => 7]);
|
|
$pingTextResponseCode = wp_remote_retrieve_response_code($responsePingText);
|
|
|
|
if ($responseCode == 500) {
|
|
$log[] = 'The response was a *500 Internal Server Error*. There can be different reasons for that. ' .
|
|
'Lets dig a bit deeper...';
|
|
}
|
|
|
|
$log[] = 'Examining where the *.htaccess* rules in the ' . $rootId . ' folder points to. ';
|
|
|
|
if ($rulesPointsToWod) {
|
|
$log[] = 'They point to **wod**/webp-on-demand.php';
|
|
} elseif ($rulesPointsToWod2) {
|
|
$log[] = 'They point to **wod2**/webp-on-demand.php';
|
|
} else {
|
|
$log[] = '**There are no redirect rule to *webp-on-demand.php* in the .htaccess!**{: .warn}';
|
|
$log[] = 'Here is the rules:';
|
|
$log = array_merge($log, $htaccessRules);
|
|
}
|
|
|
|
if ($rulesPointsToWod) {
|
|
$log[] = 'Requesting simple test script "wod/ping.php"... ' .
|
|
'Result: ' . ($pingPhpResponseCode == '200' ? 'ok' : 'failed (response code: ' . $pingPhpResponseCode . ')');
|
|
//'Result: ' . ($canRunTestScriptInWod ? 'ok' : 'failed');
|
|
|
|
if ($canRunTestScriptInWod) {
|
|
if ($responseCode == '500') {
|
|
$log[] = '';
|
|
$log[] = '**As the test script works, it would seem that the explanation for the 500 internal server ' .
|
|
'error is that the PHP script (webp-on-demand.php) crashes. ' .
|
|
'You can help me by enabling debugging and post the error on the support forum on Wordpress ' .
|
|
'(https://wordpress.org/support/plugin/webp-express/), or create an issue on github ' .
|
|
'(https://github.com/rosell-dk/webp-express/issues)**';
|
|
$log[] = '';
|
|
}
|
|
} else {
|
|
$log[] = 'Requesting simple test file "wod/ping.txt". ' .
|
|
'Result: ' . ($pingTextResponseCode == '200' ? 'ok' : 'failed (response code: ' . $pingTextResponseCode . ')');
|
|
|
|
if ($canRunTestScriptInWod2) {
|
|
if ($responseCode == 500) {
|
|
if ($pingTextResponseCode == '500') {
|
|
$log[] = 'The problem appears to be that the *.htaccess* placed in *plugins/webp-express/wod/.htaccess*' .
|
|
' contains auth directives ("Allow" and "Request") and your server is set up to go fatal about it. ' .
|
|
'Luckily, it seems that running scripts in the "wod2" folder works. ' .
|
|
'**What you need to do is simply to click the "Save settings and force new .htacess rules"' .
|
|
' button. WebP Express wil then change the .htaccess rules to point to the "wod2" folder**';
|
|
} else {
|
|
$log[] = 'The problem appears to be running PHP scripts in the "wod". ' .
|
|
'Luckily, it seems that running scripts in the "wod2" folder works ' .
|
|
'(it has probably something to do with the *.htaccess* file placed in "wod"). ' .
|
|
'**What you need to do is simply to click the "Save settings and force new .htacess rules"' .
|
|
' button. WebP Express wil then change the .htaccess rules to point to the "wod2" folder**';
|
|
}
|
|
} elseif ($responseCode == 403) {
|
|
$log[] = 'The problem appears to be running PHP scripts in the "wod". ' .
|
|
'Luckily, it seems that running scripts in the "wod2" folder works ' .
|
|
'(it could perhaps have something to do with the *.htaccess* file placed in "wod", ' .
|
|
'although it ought not result in a 403). **What you need to do is simply to click the "Save settings and force new .htacess rules"' .
|
|
' button. WebP Express wil then change the .htaccess rules to point to the "wod2" folder**';
|
|
}
|
|
|
|
return $log;
|
|
}
|
|
}
|
|
}
|
|
|
|
$log[] = 'Requesting simple test script "wod2/ping.php". Result: ' . ($canRunTestScriptInWod2 ? 'ok' : 'failed');
|
|
$responsePingText2 = wp_remote_get(Paths::getPluginsUrl() . '/webp-express/wod2/ping.txt', ['timeout' => 7]);
|
|
$pingTextResponseCode2 = wp_remote_retrieve_response_code($responsePingText2);
|
|
$log[] = 'Requesting simple test file "wod2/ping.txt". ' .
|
|
'Result: ' . ($pingTextResponseCode == '200' ? 'ok' : 'failed (response code: ' . $pingTextResponseCode2 . ')');
|
|
|
|
if ($rulesPointsToWod2) {
|
|
if ($canRunTestScriptInWod2) {
|
|
if ($responseCode == '500') {
|
|
$log[] = '';
|
|
$log[] = '**As the test script works, it would seem that the explanation for the 500 internal server ' .
|
|
'error is that the PHP script (webp-on-demand.php) crashes. ' .
|
|
'You can help me by enabling debugging and post the error on the support forum on Wordpress ' .
|
|
'(https://wordpress.org/support/plugin/webp-express/), or create an issue on github ' .
|
|
'(https://github.com/rosell-dk/webp-express/issues)**';
|
|
$log[] = '';
|
|
}
|
|
} else {
|
|
if ($canRunTestScriptInWod) {
|
|
$log[] = '';
|
|
$log[] = 'The problem appears to be running PHP scripts in the "wod2" folder. ' .
|
|
'Luckily, it seems that running scripts in the "wod" folder works ' .
|
|
'**What you need to do is simply to click the "Save settings and force new .htacess rules"' .
|
|
' button. WebP Express wil then change the .htaccess rules to point to the "wod" folder**';
|
|
$log[] = '';
|
|
} else {
|
|
if ($responseCode == 500) {
|
|
|
|
if ($pingTextResponseCode2 == '500') {
|
|
$log[] = 'All our requests results in 500 Internal Error. Even ' .
|
|
'the request to plugins/webp-express/wod2/ping.txt. ' .
|
|
'Surprising!';
|
|
} else {
|
|
$log[] = 'The internal server error happens for php files, but not txt files. ' .
|
|
'It could be the result of a restrictive server configuration or the works of a security plugin. ' .
|
|
'Try to examine the .htaccess file in the plugins folder and its parent folders. ' .
|
|
'Or try to look in the httpd.conf. Look for the "AllowOverride" and the "AllowOverrideList" directives. ';
|
|
}
|
|
|
|
//$log[] = 'We get *500 Internal Server Error*';
|
|
/*
|
|
It can for example be that the *.htaccess* ' .
|
|
'in the ' . $rootId . ' folder (or a parent folder) contains directives that the server either ' .
|
|
'doesnt support or has not allowed (using AllowOverride in ie httpd.conf). It could also be that the redirect succeded, ' .
|
|
'but the *.htaccess* in the folder of the script (or a parent folder) results in such problems. Also, ' .
|
|
'it could be that the script (webp-on-demand.php) for some reason fails.';
|
|
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
return $log;
|
|
}
|
|
}
|