Skip to content

Commit

Permalink
Merge pull request #874 from M0rgan01/runPhpQuery-improvements
Browse files Browse the repository at this point in the history
Improve "runPhpQuery" method
  • Loading branch information
Quetzacoalt91 authored Sep 5, 2024
2 parents 331b4db + 8b055e6 commit 4a50c22
Show file tree
Hide file tree
Showing 2 changed files with 244 additions and 24 deletions.
116 changes: 92 additions & 24 deletions classes/UpgradeTools/CoreUpgrader/CoreUpgrader.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

use Cache;
use Exception;
use InvalidArgumentException;
use ParseError;
use PrestaShop\Module\AutoUpgrade\Exceptions\UpgradeException;
use PrestaShop\Module\AutoUpgrade\Log\LoggerInterface;
use PrestaShop\Module\AutoUpgrade\Parameters\UpgradeConfiguration;
Expand Down Expand Up @@ -394,33 +396,66 @@ protected function runQuery(string $upgrade_file, string $query): void
$this->runSqlQuery($upgrade_file, $query);
}

protected function runPhpQuery(string $upgrade_file, string $query): void
/**
* @param string $query
*
* @return false|string
*/
private function extractPhpStringFromQuery(string $query)
{
// Parsing php code
$pos = strpos($query, '/* PHP:') + strlen('/* PHP:');
$phpString = substr($query, $pos, strlen($query) - $pos - strlen(' */;'));
$php = explode('::', $phpString);

return substr($query, $pos, strlen($query) - $pos - strlen(' */;'));
}

/**
* @param string $phpString
*
* @return string
*/
private function extractParametersAsString(string $phpString): string
{
preg_match('/\((.*)\)/', $phpString, $pattern);
$paramsString = trim($pattern[0], '()');
preg_match_all('/([^,]+),? ?/', $paramsString, $parameters);
// TODO: Could be `$parameters = $parameters[1] ?? [];` if PHP min version was > 7.0
$parameters = isset($parameters[1]) ?
$parameters[1] :
[];
foreach ($parameters as &$parameter) {
$parameter = str_replace('\'', '', $parameter);

return $pattern[0];
}

/**
* @param string $paramsString
*
* @return array<mixed>
*/
private function extractParametersFromString(string $paramsString): array
{
$paramsString = trim($paramsString, '()');
$code = "return [$paramsString];";

try {
$parameters = eval($code);
} catch (ParseError $e) {
throw new InvalidArgumentException('Error while parsing the parameter string.');
}

return $parameters;
}

protected function runPhpQuery(string $upgrade_file, string $query): void
{
// Parsing php code
$phpString = $this->extractPhpStringFromQuery($query);
$php = explode('::', $phpString);
$stringParameters = $this->extractParametersAsString($phpString);
$parameters = $this->extractParametersFromString($stringParameters);

// reset phpRes to a null value
$phpRes = null;
// Call a simple function
if (strpos($phpString, '::') === false) {
$func_name = str_replace($pattern[0], '', $php[0]);
$func_name = str_replace($stringParameters, '', $php[0]);
$pathToPhpDirectory = $this->pathToUpgradeScripts . 'php/';

if (!file_exists($pathToPhpDirectory . strtolower($func_name) . '.php')) {
$this->logger->error('[ERROR] ' . $pathToPhpDirectory . strtolower($func_name) . ' PHP - missing file ' . $query);
$this->container->getState()->setWarningExists(true);
$this->logMissingFileError($pathToPhpDirectory, $func_name, $query);

return;
}
Expand All @@ -430,24 +465,57 @@ protected function runPhpQuery(string $upgrade_file, string $query): void
}
// Or an object method
else {
$func_name = [$php[0], str_replace($pattern[0], '', $php[1])];
$this->logger->error('[ERROR] ' . $upgrade_file . ' PHP - Object Method call is forbidden (' . $php[0] . '::' . str_replace($pattern[0], '', $php[1]) . ')');
$this->container->getState()->setWarningExists(true);
$this->logForbiddenObjectMethodError($phpString, $upgrade_file);

return;
}

if (isset($phpRes) && (is_array($phpRes) && !empty($phpRes['error'])) || $phpRes === false) {
$this->logger->error('
[ERROR] PHP ' . $upgrade_file . ' ' . $query . "\n" . '
' . (empty($phpRes['error']) ? '' : $phpRes['error'] . "\n") . '
' . (empty($phpRes['msg']) ? '' : ' - ' . $phpRes['msg'] . "\n"));
$this->container->getState()->setWarningExists(true);
if ($this->hasPhpError($phpRes)) {
$this->logPhpError($upgrade_file, $query, $phpRes);
} else {
$this->logger->debug('<div class="upgradeDbOk">[OK] PHP ' . $upgrade_file . ' : ' . $query . '</div>');
}
}

/**
* @param mixed $phpRes
*
* @return bool
*/
private function hasPhpError($phpRes): bool
{
return isset($phpRes) && (is_array($phpRes) && !empty($phpRes['error'])) || $phpRes === false;
}

/**
* @param string $upgrade_file
* @param string $query
* @param mixed $phpRes
*
* @return void
*/
private function logPhpError(string $upgrade_file, string $query, $phpRes): void
{
$this->logger->error(
'[ERROR] PHP ' . $upgrade_file . ' ' . $query . "\n" .
(empty($phpRes['error']) ? '' : $phpRes['error'] . "\n") .
(empty($phpRes['msg']) ? '' : ' - ' . $phpRes['msg'] . "\n")
);
$this->container->getState()->setWarningExists(true);
}

private function logMissingFileError(string $path, string $func_name, string $query): void
{
$this->logger->error('[ERROR] ' . $path . strtolower($func_name) . ' PHP - missing file ' . $query);
$this->container->getState()->setWarningExists(true);
}

private function logForbiddenObjectMethodError(string $phpString, string $upgrade_file): void
{
$this->logger->error('[ERROR] ' . $upgrade_file . ' PHP - Object Method call is forbidden (' . $phpString . ')');
$this->container->getState()->setWarningExists(true);
}

protected function runSqlQuery(string $upgrade_file, string $query): void
{
if (strstr($query, 'CREATE TABLE') !== false) {
Expand Down
152 changes: 152 additions & 0 deletions tests/unit/UpgradeTools/CoreUpgrader/CoreUpgraderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/

namespace unit\UpgradeTools\CoreUpgrader;

use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use PrestaShop\Module\AutoUpgrade\Log\StreamedLogger;
use PrestaShop\Module\AutoUpgrade\UpgradeContainer;
use PrestaShop\Module\AutoUpgrade\UpgradeTools\CoreUpgrader\CoreUpgrader17;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;

class CoreUpgraderTest extends TestCase
{
/**
* @var CoreUpgrader17
*/
private $coreUpgrader;

protected function setUp()
{
$this->coreUpgrader = new CoreUpgrader17($this->createMock(UpgradeContainer::class), $this->createMock(StreamedLogger::class));
}

/**
* @throws ReflectionException
*/
protected static function getMethod($name): ReflectionMethod
{
$class = new ReflectionClass(CoreUpgrader17::class);
$method = $class->getMethod($name);
$method->setAccessible(true);

return $method;
}

/**
* @throws ReflectionException
*/
public function testExtractPhpStringFromQueryWithoutParameter()
{
$method = self::getMethod('extractPhpStringFromQuery');
$sql = '/* PHP:add_supplier_manufacturer_routes(); */;';
$methodExtracted = $method->invokeArgs($this->coreUpgrader, [$sql]);

$this->assertSame('add_supplier_manufacturer_routes();', $methodExtracted);
}

/**
* @throws ReflectionException
*/
public function testExtractPhpStringFromQueryWithParameter()
{
$method = self::getMethod('extractPhpStringFromQuery');
$sql = '/* PHP:add_supplier_manufacturer_routes(1); */;';
$methodExtracted = $method->invokeArgs($this->coreUpgrader, [$sql]);

$this->assertSame('add_supplier_manufacturer_routes(1);', $methodExtracted);

$sql = "/* PHP:add_supplier_manufacturer_routes('trotrolo'); */;";
$methodExtracted = $method->invokeArgs($this->coreUpgrader, [$sql]);

$this->assertSame("add_supplier_manufacturer_routes('trotrolo');", $methodExtracted);

$sql = '/* PHP:add_supplier_manufacturer_routes("trotr\'olo\'"); */;';
$methodExtracted = $method->invokeArgs($this->coreUpgrader, [$sql]);

$this->assertSame('add_supplier_manufacturer_routes("trotr\'olo\'");', $methodExtracted);
}

/**
* @throws ReflectionException
*/
public function testExtractParametersAsString()
{
$method = self::getMethod('extractParametersAsString');
$phpString = "bestMethodName('trololo');";
$stringExtracted = $method->invokeArgs($this->coreUpgrader, [$phpString]);

$this->assertSame("('trololo')", $stringExtracted);
}

/**
* @throws ReflectionException
*/
public function testExtractParametersFromPhpString()
{
$method = self::getMethod('extractParametersFromString');
$phpString = "('jack')";
$parametersExtracted = $method->invokeArgs($this->coreUpgrader, [$phpString]);

$this->assertSame(['jack'], $parametersExtracted);

$method = self::getMethod('extractParametersFromString');
$phpString = "('jack', [1,2,3,4])";
$parametersExtracted = $method->invokeArgs($this->coreUpgrader, [$phpString]);

$this->assertSame(['jack', [1, 2, 3, 4]], $parametersExtracted);

$method = self::getMethod('extractParametersFromString');
$phpString = "('feature_flag', 'stability', 'VARCHAR(64) DEFAULT \'beta\' NOT NULL')";
$parametersExtracted = $method->invokeArgs($this->coreUpgrader, [$phpString]);

$this->assertSame(['feature_flag', 'stability', 'VARCHAR(64) DEFAULT \'beta\' NOT NULL'], $parametersExtracted);
}

/**
* @throws ReflectionException
*/
public function testExtractParametersFromPhpStringParsingExceptions()
{
$method = self::getMethod('extractParametersFromString');
$phpString = '($this->)';

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Error while parsing the parameter string.');

$method->invokeArgs($this->coreUpgrader, [$phpString]);

$phpString = '(return 1;)';

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Error while parsing the parameter string.');

$method->invokeArgs($this->coreUpgrader, [$phpString]);
}
}

0 comments on commit 4a50c22

Please sign in to comment.