Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 72 additions & 4 deletions core/components/minishop3/phinx.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,90 @@
die('MODX_CORE_PATH not defined in config.core.php');
}

if (!class_exists('modX')) {
$modxClass = 'MODX\\Revolution\\modX';

if (!class_exists($modxClass)) {
require_once MODX_CORE_PATH . 'model/modx/modx.class.php';
}

$modx = new modX();
if (!class_exists($modxClass) && class_exists('modX')) {
$modxClass = 'modX';
}

$modx = new $modxClass();
$modx->initialize('mgr');
}

if (!function_exists('ms3PhinxExtractDsnCharset')) {
function ms3PhinxExtractDsnCharset(?string $dsn): ?string
{
if ($dsn === null || $dsn === '') {
return null;
}

if (preg_match('/(?:^|;)charset=([^;]+)/i', $dsn, $matches) !== 1) {
return null;
}

return trim($matches[1]);
}
}

if (!function_exists('ms3PhinxNormalizeMysqlCharset')) {
function ms3PhinxNormalizeMysqlCharset(?string $charset, bool $preferUtf8mb4 = false): string
{
$normalized = strtolower(trim((string) $charset));
$normalized = str_replace('-', '', $normalized);

return match ($normalized) {
'', 'utf8', 'utf8mb3' => $preferUtf8mb4 ? 'utf8mb4' : 'utf8',
'utf8mb4' => 'utf8mb4',
default => preg_replace('/[^a-z0-9_]/', '', $normalized) ?: 'utf8mb4',
};
}
}

if (!function_exists('ms3PhinxDefaultMysqlCollation')) {
function ms3PhinxDefaultMysqlCollation(string $charset): string
{
return match ($charset) {
'utf8' => 'utf8_general_ci',
'utf8mb4' => 'utf8mb4_unicode_ci',
default => $charset . '_general_ci',
};
}
}

$dsnCharset = ms3PhinxExtractDsnCharset($modx->getOption('database_dsn', null, null));
$databaseCharset = $modx->getOption('database_charset', null, null);

if ($dsnCharset !== null) {
$mysqlCharset = ms3PhinxNormalizeMysqlCharset($dsnCharset);
} elseif ($databaseCharset !== null && trim((string) $databaseCharset) !== '') {
$mysqlCharset = ms3PhinxNormalizeMysqlCharset($databaseCharset);
} else {
$mysqlCharset = ms3PhinxNormalizeMysqlCharset($modx->getOption('charset', null, 'utf8mb4'), true);
}

$mysqlCollation = $modx->getOption(
'database_collation',
null,
$modx->getOption(
'collation',
null,
ms3PhinxDefaultMysqlCollation($mysqlCharset)
)
);

$dbConfig = [
'adapter' => 'mysql',
'host' => $modx->getOption('host', null, 'localhost'),
'name' => $modx->getOption('dbname'),
'user' => $modx->getOption('username'),
'pass' => $modx->getOption('password'),
'port' => $modx->getOption('port', null, '3306'),
'charset' => $modx->getOption('charset', null, 'utf8mb4'),
'collation' => $modx->getOption('collation', null, 'utf8mb4_unicode_ci'),
'charset' => $mysqlCharset,
'collation' => $mysqlCollation,
'table_prefix' => $modx->getOption('table_prefix', null, ''),
];

Expand Down
84 changes: 84 additions & 0 deletions core/components/minishop3/tests/PhinxCharsetConfigTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

/**
* Static checks for Phinx MySQL charset configuration (without MODX).
*
* Run: php tests/PhinxCharsetConfigTest.php
*/

declare(strict_types=1);

$fail = static function (string $message): never {
fwrite(STDERR, "FAIL: {$message}\n");
exit(1);
};

$buildConfig = static function (array $options): array {
$modx = new class ($options) {
public function __construct(private readonly array $options)
{
}

public function getOption(string $key, mixed $options = null, mixed $default = null): mixed
{
return $this->options[$key] ?? $default;
}
};

return require __DIR__ . '/../phinx.php';
};

$config = $buildConfig([
'dbname' => 'minishop3',
'username' => 'user',
'password' => 'secret',
'charset' => 'UTF-8',
'table_prefix' => 'modx_',
]);

$charset = $config['environments']['production']['charset'] ?? null;
if ($charset !== 'utf8mb4') {
$fail("MODX charset UTF-8 must be normalized to MySQL charset utf8mb4, got {$charset}");
}

$collation = $config['environments']['production']['collation'] ?? null;
if ($collation !== 'utf8mb4_unicode_ci') {
$fail("utf8mb4 charset must use utf8mb4_unicode_ci by default, got {$collation}");
}

$config = $buildConfig([
'dbname' => 'minishop3',
'username' => 'user',
'password' => 'secret',
'database_dsn' => 'mysql:host=localhost;dbname=minishop3;charset=utf8',
'charset' => 'UTF-8',
'table_prefix' => 'modx_',
]);

$charset = $config['environments']['production']['charset'] ?? null;
if ($charset !== 'utf8') {
$fail("database_dsn charset must override MODX web charset, got {$charset}");
}

$config = $buildConfig([
'dbname' => 'minishop3',
'username' => 'user',
'password' => 'secret',
'database_charset' => '',
'database_collation' => 'utf8mb4_general_ci',
'charset' => 'UTF-8',
'table_prefix' => 'modx_',
]);

$charset = $config['environments']['production']['charset'] ?? null;
if ($charset !== 'utf8mb4') {
$fail("empty database_charset must fall back to normalized MODX charset, got {$charset}");
}

$collation = $config['environments']['production']['collation'] ?? null;
if ($collation !== 'utf8mb4_general_ci') {
$fail("database_collation must override default collation, got {$collation}");
}

fwrite(STDOUT, "OK PhinxCharsetConfigTest\n");
exit(0);