yii2-queue
yii2-queue copied to clipboard
src/serializers/JsonSerializer.php doesn't work if a class has parameters in the constructor
What steps will reproduce the problem?
Try to serialize an active record which has a datetime column with its default value
What's expected?
Serialize the object and then successfully unserializing it.
What do you get instead?
When you try to unserialize, you get an error because yii\db\Expression is missing required parameters in the constructor
Additional info
I have an idea to use reflection to get around the issue. Of course it will only work if the parameters are public properties. I will post here if it works.
<?php
class JsonSerializer extends \yii\queue\serializers\JsonSerializer
{
public $constructorParamsKey = 'constructorParams';
/**
* @param mixed $data
* @return array|mixed
* @throws InvalidConfigException
*/
protected function toArray($data)
{
if (is_object($data)) {
$result = [$this->classKey => get_class($data)];
foreach (get_object_vars($data) as $property => $value) {
if ($property === $this->classKey) {
throw new InvalidConfigException("Object cannot contain $this->classKey property.");
}
if ($property === $this->constructorParamsKey) {
throw new InvalidConfigException("Object cannot contain $this->constructorParamsKey property.");
}
$result[$property] = $this->toArray($value);
}
$constructorParams = $this->getConstructorParams($data);
if(!empty($constructorParams)){
$result['constructorParams'] = $constructorParams;
}
return $result;
}
if (is_array($data)) {
$result = [];
foreach ($data as $key => $value) {
if ($key === $this->classKey) {
throw new InvalidConfigException("Array cannot contain $this->classKey key.");
}
$result[$key] = $this->toArray($value);
}
return $result;
}
return $data;
}
/**
* @param array $data
* @return mixed
*/
protected function fromArray($data)
{
if (!is_array($data)) {
return $data;
}
if (!isset($data[$this->classKey])) {
$result = [];
foreach ($data as $key => $value) {
$result[$key] = $this->fromArray($value);
}
return $result;
}
$config = ['class' => $data[$this->classKey]];
unset($data[$this->classKey]);
$constructorParams = [];
if(isset($data['constructorParams'])){
$constructorParams = $data['constructorParams'];
unset($data['constructorParams']);
}
foreach ($data as $property => $value) {
$config[$property] = $this->fromArray($value);
}
return Yii::createObject($config, $constructorParams);
}
/**
* Returns the constructor params and their values
* @param $object
* @return array
*/
protected function getConstructorParams($object)
{
$ref = new ReflectionClass($object);
if (!$ref->isInstantiable()) {
return [];
}
$parameters = [];
$constructor = $ref->getConstructor();
$params = $constructor->getParameters();
foreach ($params as $param) {
$name = $param->getName();
if(property_exists($object, $name)) {
$parameters[] = $object->{$name};
}
}
return $parameters;
}
}
| Q | A |
|---|---|
| Yii version | 2.0.15.1 |
| PHP version | 7.2.34 |
| Operating system | Linux |
Why you're using JsonSerializer? It is intended to be used for serializing simple jobs, and serializing ActiveRecord is way beyond "simple".
Why you're using
JsonSerializer? It is intended to be used for serializing simple jobs, and serializingActiveRecordis way beyond "simple".
I'm not syncing the whole active record. Of course this would not be optimal. I use the queue to log a history of the changes of attributes. I only want to save a json field in the db with the changed attributes. And I have a column which is a datetime field, and I got this error, so I decided to report it.
I could use the PHP serializer, but JSON is better, because it is reusable with other languages and storage engines.