Add X13 WebP module for image conversion to next-generation formats

- Implemented the main module class with essential properties and methods.
- Added translation support for various user interface strings.
- Created XML configuration file for module versioning.
- Ensured compatibility with different PHP versions and PrestaShop versions.
This commit is contained in:
2025-10-16 21:30:24 +02:00
parent d220e35c31
commit 3b29a79921
235 changed files with 40220 additions and 189 deletions

View File

@@ -0,0 +1,39 @@
<?php
namespace ExecWithFallback;
/**
* Check if any of the methods are available on the system.
*
* @package ExecWithFallback
* @author Bjørn Rosell <it@rosell.dk>
*/
class Availability extends ExecWithFallback
{
/**
* Check if any of the methods are available on the system.
*
* @param boolean $needResultCode Whether the code using this library is going to supply $result_code to the exec
* call. This matters because shell_exec is only available when not.
*/
public static function anyAvailable($needResultCode = true)
{
foreach (self::$methods as $method) {
if (self::methodAvailable($method, $needResultCode)) {
return true;
}
}
return false;
}
public static function methodAvailable($method, $needResultCode = true)
{
if (!ExecWithFallback::functionEnabled($method)) {
return false;
}
if ($needResultCode) {
return ($method != 'shell_exec');
}
return true;
}
}

View File

@@ -0,0 +1,127 @@
<?php
namespace ExecWithFallback;
/**
* Execute command with exec(), open_proc() or whatever available
*
* @package ExecWithFallback
* @author Bjørn Rosell <it@rosell.dk>
*/
class ExecWithFallback
{
protected static $methods = ['exec', 'passthru', 'popen', 'proc_open', 'shell_exec'];
/**
* Check if any of the methods are available on the system.
*
* @param boolean $needResultCode Whether the code using this library is going to supply $result_code to the exec
* call. This matters because shell_exec is only available when not.
*/
public static function anyAvailable($needResultCode = true)
{
return Availability::anyAvailable($needResultCode);
}
/**
* Check if a function is enabled (function_exists as well as ini is tested)
*
* @param string $functionName The name of the function
*
* @return boolean If the function is enabled
*/
public static function functionEnabled($functionName)
{
if (!function_exists($functionName)) {
return false;
}
if (function_exists('ini_get')) {
if (ini_get('safe_mode')) {
return false;
}
$d = ini_get('disable_functions') . ',' . ini_get('suhosin.executor.func.blacklist');
if ($d === false) {
$d = '';
}
$d = preg_replace('/,\s*/', ',', $d);
if (strpos(',' . $d . ',', ',' . $functionName . ',') !== false) {
return false;
}
}
return is_callable($functionName);
}
/**
* Execute. - A substitute for exec()
*
* Same signature and results as exec(): https://www.php.net/manual/en/function.exec.php
* In case neither exec(), nor emulations are available, it throws an Exception.
* This is more gentle than real exec(), which on some systems throws a FATAL when exec() is disabled
* If you want the more acurate substitute, which might halt execution, use execNoMercy() instead.
*
* @param string $command The command to execute
* @param string &$output (optional)
* @param int &$result_code (optional)
*
* @return string | false The last line of output or false in case of failure
* @throws \Exception If no methods are available
*/
public static function exec($command, &$output = null, &$result_code = null)
{
foreach (self::$methods as $method) {
if (self::functionEnabled($method)) {
if (func_num_args() >= 3) {
if ($method == 'shell_exec') {
continue;
}
$result = self::runExec($method, $command, $output, $result_code);
} else {
$result = self::runExec($method, $command, $output);
}
if ($result !== false) {
return $result;
}
}
}
if (isset($result) && ($result === false)) {
return false;
}
throw new \Exception('exec() is not available');
}
/**
* Execute. - A substitute for exec(), with exact same errors thrown if exec() is missing.
*
* Danger: On some systems, this results in a fatal (non-catchable) error.
*/
public static function execNoMercy($command, &$output = null, &$result_code = null)
{
if (func_num_args() == 3) {
return ExecWithFallbackNoMercy::exec($command, $output, $result_code);
} else {
return ExecWithFallbackNoMercy::exec($command, $output);
}
}
public static function runExec($method, $command, &$output = null, &$result_code = null)
{
switch ($method) {
case 'exec':
return exec($command, $output, $result_code);
case 'passthru':
return Passthru::exec($command, $output, $result_code);
case 'popen':
return POpen::exec($command, $output, $result_code);
case 'proc_open':
return ProcOpen::exec($command, $output, $result_code);
case 'shell_exec':
if (func_num_args() == 4) {
return ShellExec::exec($command, $output, $result_code);
} else {
return ShellExec::exec($command, $output);
}
}
return false;
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace ExecWithFallback;
/**
* Execute command with exec(), open_proc() or whatever available
*
* @package ExecWithFallback
* @author Bjørn Rosell <it@rosell.dk>
*/
class ExecWithFallbackNoMercy
{
/**
* Execute. - A substitute for exec()
*
* Same signature and results as exec(): https://www.php.net/manual/en/function.exec.php
*
* This is our hardcore version of our exec(). It does not merely throw an Exception, if
* no methods are available. It calls exec().
* This ensures exactly same behavior as normal exec() - the same error is thrown.
* You might want that. But do you really?
* DANGER: On some systems, calling a disabled exec() results in a fatal (non-catchable) error.
*
* @param string $command The command to execute
* @param string &$output (optional)
* @param int &$result_code (optional)
*
* @return string | false The last line of output or false in case of failure
* @throws \Exception|\Error If no methods are available. Note: On some systems, it is FATAL!
*/
public static function exec($command, &$output = null, &$result_code = null)
{
foreach (self::$methods as $method) {
if (self::functionEnabled($method)) {
if (func_num_args() >= 3) {
if ($method == 'shell_exec') {
continue;
}
$result = self::runExec($method, $command, $output, $result_code);
} else {
$result = self::runExec($method, $command, $output);
}
if ($result !== false) {
return $result;
}
}
}
if (isset($result) && ($result === false)) {
return false;
}
// MIGHT THROW FATAL!
return exec($command, $output, $result_code);
}
}

View File

@@ -0,0 +1,60 @@
<?php
namespace ExecWithFallback;
/**
* Emulate exec() with system()
*
* @package ExecWithFallback
* @author Bjørn Rosell <it@rosell.dk>
*/
class POpen
{
/**
* Emulate exec() with system()
*
* @param string $command The command to execute
* @param string &$output (optional)
* @param int &$result_code (optional)
*
* @return string | false The last line of output or false in case of failure
*/
public static function exec($command, &$output = null, &$result_code = null)
{
$handle = @popen($command, "r");
if ($handle === false) {
return false;
}
$result = '';
while (!@feof($handle)) {
$result .= fread($handle, 1024);
}
//Note: Unix Only:
// pclose() is internally implemented using the waitpid(3) system call.
// To obtain the real exit status code the pcntl_wexitstatus() function should be used.
$result_code = pclose($handle);
$theOutput = preg_split('/\s*\r\n|\s*\n\r|\s*\n|\s*\r/', $result);
// remove the last element if it is blank
if ((count($theOutput) > 0) && ($theOutput[count($theOutput) -1] == '')) {
array_pop($theOutput);
}
if (count($theOutput) == 0) {
return '';
}
if (gettype($output) == 'array') {
foreach ($theOutput as $line) {
$output[] = $line;
}
} else {
$output = $theOutput;
}
return $theOutput[count($theOutput) -1];
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace ExecWithFallback;
/**
* Emulate exec() with passthru()
*
* @package ExecWithFallback
* @author Bjørn Rosell <it@rosell.dk>
*/
class Passthru
{
/**
* Emulate exec() with passthru()
*
* @param string $command The command to execute
* @param string &$output (optional)
* @param int &$result_code (optional)
*
* @return string | false The last line of output or false in case of failure
*/
public static function exec($command, &$output = null, &$result_code = null)
{
ob_start();
// Note: We use try/catch in order to close output buffering in case it throws
try {
passthru($command, $result_code);
} catch (\Exception $e) {
ob_get_clean();
passthru($command, $result_code);
} catch (\Throwable $e) {
ob_get_clean();
passthru($command, $result_code);
}
$result = ob_get_clean();
// split new lines. Also remove trailing space, as exec() does
$theOutput = preg_split('/\s*\r\n|\s*\n\r|\s*\n|\s*\r/', $result);
// remove the last element if it is blank
if ((count($theOutput) > 0) && ($theOutput[count($theOutput) -1] == '')) {
array_pop($theOutput);
}
if (count($theOutput) == 0) {
return '';
}
if (gettype($output) == 'array') {
foreach ($theOutput as $line) {
$output[] = $line;
}
} else {
$output = $theOutput;
}
return $theOutput[count($theOutput) -1];
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace ExecWithFallback;
/**
* Emulate exec() with proc_open()
*
* @package ExecWithFallback
* @author Bjørn Rosell <it@rosell.dk>
*/
class ProcOpen
{
/**
* Emulate exec() with proc_open()
*
* @param string $command The command to execute
* @param string &$output (optional)
* @param int &$result_code (optional)
*
* @return string | false The last line of output or false in case of failure
*/
public static function exec($command, &$output = null, &$result_code = null)
{
$descriptorspec = array(
//0 => array("pipe", "r"),
1 => array("pipe", "w"),
//2 => array("pipe", "w"),
//2 => array("file", "/tmp/error-output.txt", "a")
);
$cwd = getcwd(); // or is "/tmp" better?
$processHandle = proc_open($command, $descriptorspec, $pipes, $cwd);
$result = "";
if (is_resource($processHandle)) {
// Got this solution here:
// https://stackoverflow.com/questions/5673740/php-or-apache-exec-popen-system-and-proc-open-commands-do-not-execute-any-com
//fclose($pipes[0]);
$result = stream_get_contents($pipes[1]);
fclose($pipes[1]);
//fclose($pipes[2]);
$result_code = proc_close($processHandle);
// split new lines. Also remove trailing space, as exec() does
$theOutput = preg_split('/\s*\r\n|\s*\n\r|\s*\n|\s*\r/', $result);
// remove the last element if it is blank
if ((count($theOutput) > 0) && ($theOutput[count($theOutput) -1] == '')) {
array_pop($theOutput);
}
if (count($theOutput) == 0) {
return '';
}
if (gettype($output) == 'array') {
foreach ($theOutput as $line) {
$output[] = $line;
}
} else {
$output = $theOutput;
}
return $theOutput[count($theOutput) -1];
} else {
return false;
}
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace ExecWithFallback;
/**
* Emulate exec() with system()
*
* @package ExecWithFallback
* @author Bjørn Rosell <it@rosell.dk>
*/
class ShellExec
{
/**
* Emulate exec() with shell_exec()
*
* @param string $command The command to execute
* @param string &$output (optional)
* @param int &$result_code (optional)
*
* @return string | false The last line of output or false in case of failure
*/
public static function exec($command, &$output = null, &$result_code = null)
{
$resultCodeSupplied = (func_num_args() >= 3);
if ($resultCodeSupplied) {
throw new \Exception('ShellExec::exec() does not support $result_code argument');
}
$result = shell_exec($command);
// result:
// - A string containing the output from the executed command,
// - false if the pipe cannot be established
// - or null if an error occurs or the command produces no output.
if ($result === false) {
return false;
}
if (is_null($result)) {
// hm, "null if an error occurs or the command produces no output."
// What were they thinking?
// And yes, it does return null, when no output, which is confirmed in the test "echo hi 1>/dev/null"
// What should we do? Throw or accept?
// Perhaps shell_exec throws in newer versions of PHP instead of returning null.
// We are counting on it until proved wrong.
return '';
}
$theOutput = preg_split('/\s*\r\n|\s*\n\r|\s*\n|\s*\r/', $result);
// remove the last element if it is blank
if ((count($theOutput) > 0) && ($theOutput[count($theOutput) -1] == '')) {
array_pop($theOutput);
}
if (count($theOutput) == 0) {
return '';
}
if (gettype($output) == 'array') {
foreach ($theOutput as $line) {
$output[] = $line;
}
} else {
$output = $theOutput;
}
return $theOutput[count($theOutput) -1];
}
}