N°5279 - PHP 8.1: Migrate usages of deprecated strftime() function

In the end we made an adapter to keep using the strftime() format (https://www.php.net/manual/fr/function.strftime.php); not to ease migration but because we couldn't use \DateTime::format().
We can't use \DateTime::format() directly on the whole filename as it would also format characters that are not supposed to be. eg. "__DB__-Y-m-d-production" would become "itopdb-2023-02-09-+01:00Thu, 09 Feb 2023 11:34:01 +0100202309", mind the "production" part being converted.
This commit is contained in:
Molkobain
2023-02-10 21:49:30 +01:00
parent 35b0b16e20
commit f7ee21f1d7
10 changed files with 147 additions and 26 deletions

View File

@@ -874,11 +874,50 @@ class utils
*/
public static function DateTimeFormatToPHP($sOldDateTimeFormat)
{
$aSearch = array('%d', '%m', '%y', '%Y', '%H', '%i', '%s');
$aReplacement = array('d', 'm', 'y', 'Y', 'H', 'i', 's');
$aSearch = ['%d', '%m', '%y', '%Y', '%H', '%i', '%s'];
$aReplacement = ['d', 'm', 'y', 'Y', 'H', 'i', 's'];
return str_replace($aSearch, $aReplacement, $sOldDateTimeFormat);
}
/**
* Convert an old strtime() date/time format specification {@link https://www.php.net/manual/fr/function.strftime.php}
* to a format compatible with \DateTime::format {@link https://www.php.net/manual/fr/datetime.format.php}
*
* Example: '%Y-%m-%d %H:%M:%S' => 'Y-m-d H:i:s'
*
* Note: Not all strftime() formats can be converted, in which case they will be present in the returned string (eg. '%U' or '%W')
*
* @param string $sOldStrftimeFormat
*
* @return string
* @since 3.1.0
*/
public static function StrftimeFormatToDateTimeFormat(string $sOldStrftimeFormat): string
{
$aSearch = [
'%d', '%m', '%y', '%Y', '%H', '%M', '%S', // Most popular formats
'%a', '%A', '%e', '%j', '%u', '%w', // Day formats
'%U', '%V', '%W', // Week formats
'%b', '%B', '%h', // Month formats
'%C', '%g', '%G', // Year formats
'%k', '%I', '%l', '%p', '%P', '%r', '%R', '%T', '%X', '%z', '%Z', // Time formats
'%c', '%D', '%F', '%s', '%x', // Datetime formats
'%n', '%t', '%%', // Misc. formats
];
$aReplacement = [
'd', 'm', 'y', 'Y', 'H', 'i', 's',
'D', 'l', 'j', 'z', 'N', 'w',
'%U', 'W', '%W',
'M', 'F', 'M',
'%C', 'y', 'Y',
'G', 'h', 'g', 'A', 'a', 'h:i:s A', 'H:i', 'H:i:s', '%X', 'O', 'T',
'%c', 'm/d/y', 'Y-m-d', 'U', '%x',
'%n', '%t', '%',
];
return str_replace($aSearch, $aReplacement, $sOldStrftimeFormat);
}
/**
* Allow to set cached config. Useful when running with {@link Parameters} for example.
* @param \Config $oConfig

View File

@@ -551,6 +551,12 @@ class LogChannels
*/
public const NOTIFICATIONS = 'notifications';
/**
* @var string Everything related to the backup / restore
* @since 3.1.0
*/
public const BACKUP = 'backup';
/**
* @since 3.0.0
*/

View File

@@ -23,7 +23,7 @@ auth_pwd = admin
# Full path or relative to current directory
#
# Formatting rules:
# %Y-%m-%d => 2011-01-25... see PHP documentation of strftime()
# %Y-%m-%d => 2011-01-25... see PHP documentation of strftime() (https://www.php.net/manual/fr/function.strftime.php)
# Placeholders:
# __HOST__ MySQL server
# __DB__ Database name

View File

@@ -69,7 +69,7 @@ function Usage($oP)
$oP->p('auth_user: login, must be administrator');
$oP->p('auth_pwd: ...');
}
$oP->p('backup_file [optional]: name of the file to store the backup into. Follows the PHP strftime format spec. The following placeholders are available: __HOST__, __DB__, __SUBNAME__');
$oP->p('backup_file [optional]: name of the file to store the backup into. Follows the PHP strftime() format spec (https://www.php.net/manual/fr/function.strftime.php). The following placeholders are available: __HOST__, __DB__, __SUBNAME__');
$oP->p('simulate [optional]: set to check the name of the file that would be created');
$oP->p('mysql_bindir [optional]: specify the path for mysqldump');

View File

@@ -108,20 +108,9 @@ function MakeArchiveFileName($iRefTime = null)
$sDefaultBackupFileName = sys_get_temp_dir().'/'."__DB__-%Y-%m-%d";
$sBackupFile = utils::ReadParam('backup_file', $sDefaultBackupFileName, true, 'raw_data');
$oConfig = GetConfig();
$sBackupFile = str_replace('__HOST__', $oConfig->Get('db_host'), $sBackupFile);
$sBackupFile = str_replace('__DB__', $oConfig->Get('db_name'), $sBackupFile);
$sBackupFile = str_replace('__SUBNAME__', $oConfig->Get('db_subname'), $sBackupFile);
if (is_null($iRefTime))
{
$sBackupFile = strftime($sBackupFile);
}
else
{
$sBackupFile = strftime($sBackupFile, $iRefTime);
}
$oBackup = new DBBackup();
$oDateTime = $iRefTime !== null ? new DateTime($iRefTime) : new DateTime();
$sBackupFile = $oBackup->MakeName($sBackupFile, $oDateTime);
return $sBackupFile;
}

View File

@@ -163,8 +163,7 @@ class BackupExec extends AbstractWeeklyScheduledProcess
//
$oBackup->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting($this->GetModuleName(), 'mysql_bindir', ''));
$sBackupFileFormat = MetaModel::GetConfig()->GetModuleSetting($this->GetModuleName(), 'file_name_format',
'__DB__-%Y-%m-%d_%H_%M');
$sBackupFileFormat = MetaModel::GetConfig()->GetModuleSetting($this->GetModuleName(), 'file_name_format', '__DB__-%Y-%m-%d_%H_%M');
$sName = $oBackup->MakeName($sBackupFileFormat);
if ($sName == '')
{

View File

@@ -69,7 +69,7 @@ function Usage($oP)
$oP->p('auth_user: login, must be administrator');
$oP->p('auth_pwd: ...');
}
$oP->p('backup_file [optional]: name of the file to store the backup into. Follows the PHP strftime format spec. The following placeholders are available: __HOST__, __DB__, __SUBNAME__');
$oP->p('backup_file [optional]: name of the file to store the backup into. Follows the PHP strftime() (https://www.php.net/manual/fr/function.strftime.php) format spec. The following placeholders are available: __HOST__, __DB__, __SUBNAME__');
$oP->p('mysql_bindir [optional]: specify the path for mysql executable');
if (utils::IsModeCLI())
@@ -131,7 +131,7 @@ function ExecuteMainOperation($oP){
exit;
}
// Interpret strftime specifications (like %Y) and database placeholders
// Interpret strftime() specifications (like %Y) and database placeholders
$oRestore = new MyCliRestore($oP);
$oRestore->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));

View File

@@ -146,18 +146,35 @@ class DBBackup
/**
* Create a normalized backup name, depending on the current date/time and Database
*
* @param string sNameSpec Name and path, eventually containing itop placeholders + time formatting specs
* @param string sNameSpec Name and path, eventually containing itop placeholders + time formatting following the strftime() format {@link https://www.php.net/manual/fr/function.strftime.php}
* @param \DateTime|null $oDateTime Date time to use for the name
*
* @return string
* @since 3.1.0 N°5279 Add $oDtaeaTime parameter
*/
public function MakeName($sNameSpec = "__DB__-%Y-%m-%d")
public function MakeName($sNameSpec = "__DB__-%Y-%m-%d", DateTime $oDateTime = null)
{
if ($oDateTime === null) {
$oDateTime = new DateTime();
}
$sFileName = $sNameSpec;
$sFileName = str_replace('__HOST__', $this->sDBHost, $sFileName);
$sFileName = str_replace('__DB__', $this->sDBName, $sFileName);
$sFileName = str_replace('__SUBNAME__', $this->sDBSubName, $sFileName);
// Transform %Y, etc.
$sFileName = strftime($sFileName);
// Transform date/time placeholders (%Y, %m, etc)
// N°5279 - As of PHP 8.1 strftime() is deprecated so we use \DateTime::format() instead
//
// IMPORTANT: We can't use \DateTime::format() directly on the whole filename as it would also format characters that are not supposed to be. eg. "__DB__-Y-m-d-production" would become "itopdb-2023-02-09-+01:00Thu, 09 Feb 2023 11:34:01 +0100202309"
$sFileName = preg_replace_callback(
'/(%[a-zA-Z])/',
function ($aMatches) use ($oDateTime) {
$sDateTimeFormatPlaceholder = utils::StrftimeFormatToDateTimeFormat($aMatches[0]);
return $oDateTime->format($sDateTimeFormatPlaceholder);
},
$sFileName,
);
return $sFileName;
}

View File

@@ -423,6 +423,35 @@ class utilsTest extends ItopTestCase
];
}
/**
* @dataProvider StrftimeFormatToDateTimeFormatProvider
* @covers \utils::StrftimeFormatToDateTimeFormat
*
* @param string $sInput
* @param string $sExpectedFormat
*
* @return void
*/
public function testStrftimeFormatToDateTimeFormat(string $sInput, string $sExpectedFormat)
{
$sTestedFormat = utils::StrftimeFormatToDateTimeFormat($sInput);
$this->assertEquals($sExpectedFormat, $sTestedFormat, "DateTime format transformation for '$sInput' doesn't match. Got '$sTestedFormat', expected '$sExpectedFormat'.");
}
public function StrftimeFormatToDateTimeFormatProvider(): array
{
return [
'Standard date time' => [
'%Y-%m-%d %H:%M:%S',
'Y-m-d H:i:s',
],
'All placeholders' => [
'%d | %m | %y | %Y | %H | %M | %S | %a | %A | %e | %j | %u | %w | %U | %V | %W | %b | %B | %h | %C | %g | %G | %k | %I | %l | %p | %P | %r | %R | %T | %X | %z | %Z | %c | %D | %F | %s | %x | %n | %t | %%',
'd | m | y | Y | H | i | s | D | l | j | z | N | w | %U | W | %W | M | F | M | %C | y | Y | G | h | g | A | a | h:i:s A | H:i | H:i:s | %X | O | T | %c | m/d/y | Y-m-d | U | %x | %n | %t | %',
],
];
}
/**
* @dataProvider ToCamelCaseProvider
* @covers utils::ToCamelCase

View File

@@ -4,6 +4,7 @@ namespace Combodo\iTop\Test\UnitTest\Setup;
use CMDBSource;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
use DateTime;
use DBBackup;
use utils;
@@ -102,4 +103,45 @@ class DBBackupTest extends ItopTestCase
}
$this->assertStringEndsWith('--ssl-ca='.DBBackup::EscapeShellArg($sTestCa), $sCliArgsCapathCfg);
}
/**
* @dataProvider MakeNameProvider
* @covers \DBBackup::MakeName
*
* @param string $sInputFormat
* @param \DateTime $oBackupDateTime
* @param string $sExpectedFilename
*
* @return void
*/
public function testMakeName(string $sInputFormat, DateTime $oBackupDateTime, string $sExpectedFilename): void
{
$oBackup = new DBBackup(utils::GetConfig());
$sTestedFilename = $oBackup->MakeName($sInputFormat, $oBackupDateTime);
$this->assertEquals($sExpectedFilename, $sTestedFilename, "Backup filename for '$sInputFormat' format doesn't match. Got '$sTestedFilename', expected '$sExpectedFilename'.");
}
public function MakeNameProvider(): array
{
$oBackupDateTime = DateTime::createFromFormat('Y-m-d H:i:s', '1985-07-30 15:30:59');
return [
'Default format' => [
'itopdb-%Y-%m-%d',
$oBackupDateTime,
'itopdb-1985-07-30',
],
'With time which is a placeholder that needs to be translated (minutes defined by "%M" when its actually "i" in the transformation matrix)' => [
'itopdb-%Y-%m-%d_%H:%M:%S',
$oBackupDateTime,
'itopdb-1985-07-30_15:30:59',
],
'With user defined string that would be translated if using \DateTime::format() directly' => [
'itopdb-%Y-%m-%d-production',
$oBackupDateTime,
'itopdb-1985-07-30-production',
],
];
}
}