Passed
Push — master ( 2e81e6...ed7e83 )
by
unknown
03:52
created

DataValueDeserializer::assertCanDeserialize()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
rs 8.8571
cc 5
eloc 8
nc 4
nop 1
1
<?php
2
3
namespace DataValues\Deserializers;
4
5
use DataValues\DataValue;
6
use Deserializers\DispatchableDeserializer;
7
use Deserializers\Exceptions\DeserializationException;
8
use Deserializers\Exceptions\MissingAttributeException;
9
use Deserializers\Exceptions\MissingTypeException;
10
use Deserializers\Exceptions\UnsupportedTypeException;
11
use InvalidArgumentException;
12
use RuntimeException;
13
14
/**
15
 * @since 0.1
16
 *
17
 * @license GPL-2.0+
18
 * @author Jeroen De Dauw < [email protected] >
19
 * @author Thiemo Mättig
20
 */
21
class DataValueDeserializer implements DispatchableDeserializer {
22
23
	const TYPE_KEY = 'type';
24
	const VALUE_KEY = 'value';
25
26
	/**
27
	 * @var array Associative array mapping data type IDs to either callables returning new
28
	 *  DataValue objects, or full qualified DataValue class names.
29
	 */
30
	private $builders;
31
32
	/**
33
	 * @since 0.1, callables are supported since 1.1
34
	 *
35
	 * @param array $builders Associative array mapping data type IDs to either callables, or full
36
	 *  qualified class names. Callables must accept a single value as specified by the
37
	 *  corresponding DataValue::getArrayValue, and return a new DataValue object. DataValue classes
38
	 *  given via class name must implement a static newFromArray method doing the same.
39
	 */
40
	public function __construct( array $builders = array() ) {
41
		$this->assertAreDataValueClasses( $builders );
42
		$this->builders = $builders;
43
	}
44
45
	private function assertAreDataValueClasses( array $builders ) {
46
		foreach ( $builders as $type => $builder ) {
47
			if ( !is_string( $type )
48
				|| ( !is_callable( $builder ) && !$this->isDataValueClass( $builder ) )
49
			) {
50
				$message = '$builders must map data types to callables or class names';
51
				if ( is_string( $builder ) ) {
52
					$message .= ". '$builder' is not a DataValue class.";
53
				}
54
				throw new InvalidArgumentException( $message );
55
			}
56
		}
57
	}
58
59
	private function isDataValueClass( $class ) {
60
		return is_string( $class )
61
			&& class_exists( $class )
62
			&& in_array( DataValue::class, class_implements( $class ) );
63
	}
64
65
	/**
66
	 * @see DispatchableDeserializer::isDeserializerFor
67
	 *
68
	 * @param mixed $serialization
69
	 *
70
	 * @return bool
71
	 */
72
	public function isDeserializerFor( $serialization ) {
73
		try {
74
			$this->assertCanDeserialize( $serialization );
75
			return true;
76
		} catch ( RuntimeException $ex ) {
77
			return false;
78
		}
79
	}
80
81
	/**
82
	 * @see Deserializer::deserialize
83
	 *
84
	 * @param array $serialization
85
	 *
86
	 * @throws DeserializationException
87
	 * @return DataValue
88
	 */
89
	public function deserialize( $serialization ) {
90
		$this->assertCanDeserialize( $serialization );
91
		return $this->getDeserialization( $serialization );
92
	}
93
94
	private function assertCanDeserialize( $serialization ) {
95
		if ( !is_array( $serialization ) || !array_key_exists( self::TYPE_KEY, $serialization ) ) {
96
			throw new MissingTypeException();
97
		}
98
99
		if ( !array_key_exists( self::VALUE_KEY, $serialization ) ) {
100
			throw new MissingAttributeException( self::VALUE_KEY );
101
		}
102
103
		$type = $serialization[self::TYPE_KEY];
104
		if ( !array_key_exists( $type, $this->builders ) ) {
105
			throw new UnsupportedTypeException( $type );
106
		}
107
	}
108
109
	/**
110
	 * @param array $serialization
111
	 *
112
	 * @throws DeserializationException
113
	 * @return DataValue
114
	 */
115
	private function getDeserialization( array $serialization ) {
116
		$type = $serialization[self::TYPE_KEY];
117
		$value = $serialization[self::VALUE_KEY];
118
		$builder = $this->builders[$type];
119
120
		try {
121
			if ( is_callable( $builder ) ) {
122
				return $builder( $value );
123
			}
124
125
			/** @var DataValue $builder */
126
			return $builder::newFromArray( $value );
127
		} catch ( InvalidArgumentException $ex ) {
128
			throw new DeserializationException( $ex->getMessage(), $ex );
129
		}
130
	}
131
132
}
133