yii2-queue icon indicating copy to clipboard operation
yii2-queue copied to clipboard

src/serializers/JsonSerializer.php doesn't work if a class has parameters in the constructor

Open TheBlueAssasin opened this issue 3 years ago • 3 comments

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

TheBlueAssasin avatar Sep 12 '22 15:09 TheBlueAssasin

Why you're using JsonSerializer? It is intended to be used for serializing simple jobs, and serializing ActiveRecord is way beyond "simple".

rob006 avatar Sep 12 '22 20:09 rob006

Why you're using JsonSerializer? It is intended to be used for serializing simple jobs, and serializing ActiveRecord is 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.

TheBlueAssasin avatar Sep 13 '22 05:09 TheBlueAssasin