diff --git a/app/Elements/PafUid.php b/app/Elements/PafUid.php index 82cc703bfed..9dd0925f95f 100644 --- a/app/Elements/PafUid.php +++ b/app/Elements/PafUid.php @@ -19,29 +19,45 @@ namespace Fisharebest\Webtrees\Elements; +use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Tree; +use function strtoupper; + /** * _UID fields, as created by PAF and other applications */ class PafUid extends AbstractElement { - protected const MAXIMUM_LENGTH = 34; - - /** - * Create a default value for this element. - * - * @param Tree $tree - * - * @return string - */ - public function default(Tree $tree): string + protected const MAXIMUM_LENGTH = 36; + + public function canonical(string $value): string { - if ($tree->getPreference('GENERATE_UIDS') === '1') { - return Registry::idFactory()->pafUid(); + $value = parent::canonical($value); + + if (preg_match('/([0-9a-f]{8})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{12})/i', $value, $match) === 1) { + $value = strtoupper($match[1] . $match[2] . $match [3] . $match[4] . $match[5]); + + return $value . Registry::idFactory()->pafUidChecksum($value); } - return ''; + return Registry::idFactory()->pafUid(); + } + + public function edit(string $id, string $name, string $value, Tree $tree): string + { + return + '
' . + parent::edit($id, $name, $value, $tree) . + '' . + '
' . + ''; } } diff --git a/app/Elements/Uid.php b/app/Elements/Uid.php index 58a4092cfc9..13191a1d9dc 100644 --- a/app/Elements/Uid.php +++ b/app/Elements/Uid.php @@ -19,9 +19,14 @@ namespace Fisharebest\Webtrees\Elements; +use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Tree; +use function e; +use function preg_match; +use function strtolower; + /** * UID fields */ @@ -29,19 +34,31 @@ class Uid extends AbstractElement { protected const MAXIMUM_LENGTH = 36; - /** - * Create a default value for this element. - * - * @param Tree $tree - * - * @return string - */ - public function default(Tree $tree): string + public function canonical(string $value): string { - if ($tree->getPreference('GENERATE_UIDS') === '1') { - return Registry::idFactory()->uuid(); + $value = strtolower(parent::canonical($value)); + + if (preg_match('/([0-9a-f]{8})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{12})/', $value, $match) === 1) { + return $match[1] . '-' . $match[2] . '-' . $match [3] . '-' . $match[4] . '-' . $match[5]; } - return ''; + return Registry::idFactory()->uuid(); + } + + + public function edit(string $id, string $name, string $value, Tree $tree): string + { + return + '
' . + parent::edit($id, $name, $value, $tree) . + '' . + '
' . + ''; } } diff --git a/app/Factories/IdFactory.php b/app/Factories/IdFactory.php index 46b9e386701..3cf6e2e7b13 100644 --- a/app/Factories/IdFactory.php +++ b/app/Factories/IdFactory.php @@ -25,7 +25,9 @@ use function dechex; use function hexdec; +use function sprintf; use function str_pad; +use function str_split; use function strtoupper; use function substr; @@ -42,7 +44,7 @@ class IdFactory implements IdFactoryInterface public function uuid(): string { try { - return strtolower(strtr(Uuid::uuid4()->toString(), ['-' => ''])); + return strtolower(Uuid::uuid4()->toString()); } catch (RandomSourceException $ex) { // uuid4() can fail if there is insufficient entropy in the system. return ''; @@ -78,23 +80,18 @@ public function pafUid(): string } /** - * @param string $uid exactly 32 hex characters - * - * @return string + * Based on the C implementation in "GEDCOM Unique Identifiers" by Gordon Clarke, dated 2007-06-08 */ public function pafUidChecksum(string $uid): string { $checksum_a = 0; // a sum of the bytes $checksum_b = 0; // a sum of the incremental values of $checksum_a - for ($i = 0; $i < 32; $i += 2) { - $checksum_a += hexdec(substr($uid, $i, 2)); - $checksum_b += $checksum_a & 0xff; + foreach (str_split($uid, 2) as $byte) { + $checksum_a += hexdec($byte); + $checksum_b += $checksum_a; } - $digit1 = str_pad(dechex($checksum_a), 2, '0', STR_PAD_LEFT); - $digit2 = str_pad(dechex($checksum_b), 2, '0', STR_PAD_LEFT); - - return strtoupper($digit1 . $digit2); + return sprintf('%02X%02X', $checksum_a & 0xff, $checksum_b & 0xff); } } diff --git a/app/Services/GedcomImportService.php b/app/Services/GedcomImportService.php index 650f44be6b0..dbd272db5ca 100644 --- a/app/Services/GedcomImportService.php +++ b/app/Services/GedcomImportService.php @@ -278,10 +278,7 @@ public function importRecord(string $gedrec, Tree $tree, bool $update): void // Add a _UID if ($tree->getPreference('GENERATE_UIDS') === '1' && !str_contains($gedrec, "\n1 _UID ")) { - $element = Registry::elementFactory()->make($type . ':_UID'); - if (!$element instanceof UnknownElement) { - $gedrec .= "\n1 _UID " . $element->default($tree); - } + $gedrec .= "\n1 _UID " . Registry::idFactory()->pafUid(); } // If the user has downloaded their GEDCOM data (containing media objects) and edited it diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 2101b543020..6a621dc3a39 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -654,12 +654,6 @@ parameters: count: 1 path: app/Factories/HeaderFactory.php - - - message: '#^Parameter \#1 \$num of function dechex expects int, float\|int given\.$#' - identifier: argument.type - count: 1 - path: app/Factories/IdFactory.php - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' identifier: foreach.nonIterable diff --git a/tests/app/Elements/PafUidTest.php b/tests/app/Elements/PafUidTest.php index 41bce89b6ae..bd2441ed322 100644 --- a/tests/app/Elements/PafUidTest.php +++ b/tests/app/Elements/PafUidTest.php @@ -36,4 +36,12 @@ public static function setupBeforeClass(): void self::$element = new PafUid('label'); } + + public function testCanonical(): void + { + self::assertSame( + 'FEF44CA3CA7543ED9A05F00591315274D810', + self::$element->canonical('fef44CA3CA7543ED9A05F005913152740000'), + ); + } } diff --git a/tests/app/Elements/UidTest.php b/tests/app/Elements/UidTest.php index 5de04b7246e..0e2b291e29f 100644 --- a/tests/app/Elements/UidTest.php +++ b/tests/app/Elements/UidTest.php @@ -34,6 +34,14 @@ public static function setupBeforeClass(): void { parent::setUpBeforeClass(); - self::$element = new PafUid('label'); + self::$element = new Uid('label'); + } + + public function testCanonical(): void + { + self::assertSame( + 'fef44ca3-ca75-43ed-9a05-f00591315274', + self::$element->canonical('FEF44ca3ca7543ed9a05f00591315274'), + ); } }