Skip to content

Commit

Permalink
Easier migration from v3
Browse files Browse the repository at this point in the history
  • Loading branch information
sorinsarca committed Dec 24, 2024
1 parent a322279 commit ff8f046
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 4 deletions.
1 change: 0 additions & 1 deletion data.txt

This file was deleted.

68 changes: 66 additions & 2 deletions src/Serializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,47 @@ public static function serialize(mixed $data, ?SecurityProviderInterface $securi
public static function unserialize(string $data, ?SecurityProviderInterface $security = null): mixed
{
self::$init || self::init();
return (new DeserializationHandler())->unserialize(self::decode($data, $security));

$skipDecode = false;
// when $data starts with @ - it indicates that it is v4 signed
if (self::$v3Compatible && ($data[0] ?? null) !== "@") {
// in v3 only the content of SerializableClosure is signed
// we must use some simple heuristics to determine if this is v3
// this will only work if the serialized data contains a closure
// the security checks will be made inside SerializableClosure::unserialize()
$skipDecode = str_contains($data, 'C:32:"Opis\Closure\SerializableClosure"');
}

// Create a new deserialization handler
$handler = new DeserializationHandler();

if (!$skipDecode) {
// current - v4
return $handler->unserialize(self::decode($data, $security));
}

// v3
if (!$security || self::$securityProvider === $security) {
return $handler->unserialize($data);
}

// we have to use the current security provider
$prevSecurity = self::$securityProvider;
self::$securityProvider = $security;

try {
return $handler->unserialize($data);
} finally {
self::$securityProvider = $prevSecurity;
}
}

/**
* Unserialize data from v3.x using a security provider (optional)
* DO NOT use this to unserialize data from v4
* This method was created in order to help with migration from v3 to v4
* @throws SecurityException
*/
public static function unserialize_v3(string $data, ?SecurityProviderInterface $security = null): mixed
{
self::$init || self::init();
Expand All @@ -154,6 +192,9 @@ public static function unserialize_v3(string $data, ?SecurityProviderInterface $
}
}

/**
* Sign data using a security provider
*/
public static function encode(string $data, ?SecurityProviderInterface $security = null): string
{
$security ??= self::$securityProvider;
Expand All @@ -164,6 +205,7 @@ public static function encode(string $data, ?SecurityProviderInterface $security
}

/**
* Extract signed data using a security provider
* @throws SecurityException
*/
public static function decode(string $data, ?SecurityProviderInterface $security = null): string
Expand Down Expand Up @@ -223,6 +265,12 @@ public static function classInfo(string $class): object
];
}

/**
* Prevent serialization boxing for specified classes
* @param string ...$class
* @return void
* @throws \ReflectionException
*/
public static function preventBoxing(string ...$class): void
{
foreach ($class as $cls) {
Expand All @@ -240,6 +288,9 @@ public static function getUnserializer(string $class): ?callable
return self::classInfo($class)->unserialize ?? null;
}

/**
* Use a generic object serializer/deserializer for specified classes
*/
public static function setObjectSerialization(string ...$class): void
{
$serialize = [CustomSplSerialization::class, "sObject"];
Expand All @@ -255,13 +306,19 @@ public static function setObjectSerialization(string ...$class): void
}
}

/**
* Use custom serialization/deserialization for a class
*/
public static function setCustomSerialization(string $class, ?callable $serialize, ?callable $unserialize): void
{
$data = self::classInfo($class);
$data->serialize = $serialize;
$data->unserialize = $unserialize;
}

/**
* Set current security provider
*/
public static function setSecurityProvider(SecurityProviderInterface|null|string $security): void
{
if (is_string($security)) {
Expand All @@ -270,12 +327,19 @@ public static function setSecurityProvider(SecurityProviderInterface|null|string
self::$securityProvider = $security;
}

/**
* Get current security provider
*/
public static function getSecurityProvider(): ?SecurityProviderInterface
{
return self::$securityProvider;
}

public static function isEnum($value): bool
/**
* Helper function to detect if a value is Enum
* @internal
*/
public static function isEnum(mixed $value): bool
{
return self::$enumExists && ($value instanceof UnitEnum);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/PHP80/V3CompatTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ private function u(string $name, SecurityProviderInterface|string|null $security
if (is_string($security)) {
$security = new DefaultSecurityProvider($security);
}
return Serializer::unserialize_v3($data, $security);
return Serializer::unserialize($data, $security);
}
}

0 comments on commit ff8f046

Please sign in to comment.