Verified Commit 9ee802a2 authored by Jan Kuchař's avatar Jan Kuchař
Browse files

Introducing safer serializer which stops on classes that does not explicitly...

Introducing safer serializer which stops on classes that does not explicitly declare if serializable
parent b1e57ed2
Pipeline #38443 passed with stage
in 18 seconds
<?php
namespace Grifart\NotSerializable;
final class SaferSerializer
{
/**
* @param mixed $dataToBeSerialized
* @return string
*/
public static function serialize($dataToBeSerialized)
{
self::check($dataToBeSerialized);
return serialize($dataToBeSerialized);
}
/**
* @param mixed $dataToBeChecked
*/
public static function check($dataToBeChecked): void
{
if (is_scalar($dataToBeChecked) || is_null($dataToBeChecked)) {
return;
}
if (!is_object($dataToBeChecked)) {
throw new \LogicException(\sprintf('You have passed a non-scalar, non-object value of type %s.', gettype($dataToBeChecked)));
}
if (! (method_exists($dataToBeChecked, '__serialize') && method_exists($dataToBeChecked, '__unserialize'))) {
throw new \LogicException(\sprintf('Object of type %s do NOT explicitly state serialization support using __serialize() && __unserialize() methods.', get_class($dataToBeChecked)));
}
// can throw exceptions, not a problem as we will call real serialize() anyway
$valuesToBeSerialized = $dataToBeChecked->__serialize();
foreach($valuesToBeSerialized as $value) {
self::check($value);
}
// as there is no way how one can check referenced properties when Serializable interface or __sleep() magic method is used,
// we consider them as case where class did NOT stated that is serializable.
}
}
\ No newline at end of file
<?php
use Grifart\NotSerializable\SaferSerializer;
use Grifart\NotSerializable\NoSerialization;
use Tester\Assert;
require __DIR__ . '/../vendor/autoload.php';
class NestedSerialization {
private object $nested;
public function __construct(object $nested){$this->nested = $nested;}
/**
* @return array{someKey: object}
*/
public function __serialize(): array
{
return [
'someKey' => $this->nested
];
}
/**
* @param array{someKey: object} $data
*/
public function __unserialize(array $data): void
{
$this->nested = $data['someKey'];
}
}
// This is the case when classic serialization & SaferSerialization behaviour is different
class SerializationNotStated {}
Assert::noError(fn() => serialize(new SerializationNotStated()));
Assert::noError(fn() => serialize(new NestedSerialization(new SerializationNotStated())));
Assert::exception(fn() => SaferSerializer::serialize(new SerializationNotStated()), LogicException::class, 'Object of type SerializationNotStated do NOT explicitly state serialization support using __serialize() && __unserialize() methods.');
Assert::exception(fn() => SaferSerializer::serialize(new NestedSerialization(new SerializationNotStated())), LogicException::class, 'Object of type SerializationNotStated do NOT explicitly state serialization support using __serialize() && __unserialize() methods.');
// Now check that we did not break anything:
class SerializationDisabled {
use NoSerialization;
}
Assert::exception(fn() => serialize(new SerializationDisabled()), LogicException::class, "The class 'SerializationDisabled' is not meant to be serialized.");
Assert::exception(fn() => serialize(new NestedSerialization(new SerializationDisabled())), LogicException::class, "The class 'SerializationDisabled' is not meant to be serialized.");
Assert::exception(fn() => SaferSerializer::serialize(new SerializationDisabled()), LogicException::class, "The class 'SerializationDisabled' is not meant to be serialized.");
Assert::exception(fn() => SaferSerializer::serialize(new NestedSerialization(new SerializationDisabled())), LogicException::class, "The class 'SerializationDisabled' is not meant to be serialized.");
class SerializationImplemented {
/** @return array{} */
public function __serialize(): array { return [];}
/** @param array{} $data */
public function __unserialize(array $data): void {}
}
Assert::noError(fn() => serialize(new SerializationImplemented()));
Assert::noError(fn() => serialize(new NestedSerialization(new SerializationImplemented())));
Assert::noError(fn() => SaferSerializer::serialize(new SerializationImplemented()));
Assert::noError(fn() => SaferSerializer::serialize(new NestedSerialization(new SerializationImplemented())));
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment