Skip to content

Commit

Permalink
Classes for type configuration.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Oddy committed May 1, 2014
1 parent b4ff9ef commit 53ef9ba
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 77 deletions.
19 changes: 19 additions & 0 deletions lib/Weasel/JsonMarshaller/Config/Type/ListType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
namespace Weasel\JsonMarshaller\Config\Type;

use Weasel\JsonMarshaller\Config\DoctrineAnnotations\JsonProperty;

class ListType extends Type {

/**
* @var Type
* @JsonProperty(type="\Weasel\JsonMarshaller\Config\Type\Type")
*/
public $valueType;

function __construct($valueType = null)
{
$this->valueType = $valueType;
}

}
20 changes: 20 additions & 0 deletions lib/Weasel/JsonMarshaller/Config/Type/MapType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
namespace Weasel\JsonMarshaller\Config\Type;

use Weasel\JsonMarshaller\Config\DoctrineAnnotations\JsonProperty;

class MapType extends ListType {

/**
* @var Type
* @JsonProperty(type="\Weasel\JsonMarshaller\Config\Type\Type")
*/
public $keyType;

function __construct($keyType = null, $valueType = null)
{
$this->keyType = $keyType;
parent::__construct($valueType);
}

}
19 changes: 19 additions & 0 deletions lib/Weasel/JsonMarshaller/Config/Type/ObjectType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
namespace Weasel\JsonMarshaller\Config\Type;

use Weasel\JsonMarshaller\Config\DoctrineAnnotations\JsonProperty;

class ObjectType extends Type {

/**
* @var string
* @JsonProperty(type="string")
*/
public $class;

function __construct($class = null)
{
$this->class = $class;
}

}
25 changes: 25 additions & 0 deletions lib/Weasel/JsonMarshaller/Config/Type/ScalarType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php
namespace Weasel\JsonMarshaller\Config\Type;

use Weasel\JsonMarshaller\Config\DoctrineAnnotations\JsonProperty;
use Weasel\JsonMarshaller\Types\JsonType;

class ScalarType extends Type {

public function __construct($typeName = null) {
$this->typeName = $typeName;
}

/**
* @var string
* @JsonProperty(type="string")
*/
public $typeName;

/**
* This is a bit of a dirty hack...
* @var JsonType
*/
public $jsonType;

}
20 changes: 20 additions & 0 deletions lib/Weasel/JsonMarshaller/Config/Type/Type.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
namespace Weasel\JsonMarshaller\Config\Type;

use Weasel\JsonMarshaller\Config\DoctrineAnnotations\JsonSubTypes;
use Weasel\JsonMarshaller\Config\DoctrineAnnotations\JsonTypeInfo;


/**
* Class Type
* @package Weasel\JsonMarshaller\Config\Type
* @JsonSubTypes({
* @JsonSubTypes\Type("\Weasel\JsonMarshaller\Config\Type\ListType"),
* @JsonSubTypes\Type("\Weasel\JsonMarshaller\Config\Type\MapType"),
* @JsonSubTypes\Type("\Weasel\JsonMarshaller\Config\Type\ScalarType"),
* })
* @JsonTypeInfo(use=JsonTypeInfo::ID_NAME, include=JsonTypeInfo::AS_PROPERTY, property="type")
*/
abstract class Type {

}
134 changes: 77 additions & 57 deletions lib/Weasel/JsonMarshaller/JsonMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
namespace Weasel\JsonMarshaller;

use Weasel\Common\Utils\ReflectionUtils;
use Weasel\JsonMarshaller\Config\Type\ListType;
use Weasel\JsonMarshaller\Config\Type\MapType;
use Weasel\JsonMarshaller\Config\Type\ObjectType;
use Weasel\JsonMarshaller\Config\Type\ScalarType;
use Weasel\JsonMarshaller\Config\Type\Type;
use Weasel\JsonMarshaller\Exception\InvalidTypeException;
use InvalidArgumentException;
use Weasel\JsonMarshaller\Types;
Expand Down Expand Up @@ -277,23 +282,23 @@ protected function _encodeObject($object, $typeInfo = null, $type = null)
break;
}
$property = $typeInfo->typeInfoProperty;
$properties[$property] = $this->_encodeValue($classId, array(TypeParser::TYPE_SCALAR, "string"));
$properties[$property] = $this->_encodeValue($classId, new ScalarType("string"));
break;
case Config\Serialization\TypeInfo::TI_AS_WRAPPER_ARRAY:
// We're actually going to encase this encoded object in an array containing the classId.
if (!isset($classId)) {
break;
}
return '[' . $this->_encodeValue($classId,
array(TypeParser::TYPE_SCALAR, 'string')) . ', ' . $this->_objectToJson($properties) . ']';
new ScalarType('string')) . ', ' . $this->_objectToJson($properties) . ']';
break;
case Config\Serialization\TypeInfo::TI_AS_WRAPPER_OBJECT:
// Very similar yo the wrapper array case, but this time it's a map from the classId to the object.
if (!isset($classId)) {
break;
}
return '{' . $this->_encodeValue($classId,
array(TypeParser::TYPE_SCALAR, 'string')) . ': ' . $this->_objectToJson($properties) . '}';
new ScalarType('string')) . ': ' . $this->_objectToJson($properties) . '}';
break;
default:
throw new \Exception("Unsupported type info storage at class level");
Expand All @@ -308,7 +313,7 @@ protected function _objectToJson($properties)
{
$elements = array();
foreach ($properties as $key => $property) {
$elements[] = $this->_encodeValue($key, array(TypeParser::TYPE_SCALAR, 'string')) . ': ' . $property;
$elements[] = $this->_encodeValue($key, new ScalarType('string')) . ': ' . $property;
}
return '{' . implode(', ', $elements) . '}';
}
Expand Down Expand Up @@ -473,58 +478,82 @@ protected function _decodeClass($array, $class, $strict)

}

/**
* @param Type $type
* @return Type
*/
protected function _parseType($type) {
if (!is_array($type)) {
if (!$type instanceof Type) {
if (defined('E_USER_DEPRECATED')) {
// TODO: need to handle serialized configs before we can properly deprecate this.
// trigger_error("Use of unexpanded types is deprecated", E_USER_DEPRECATED);
}
// This is the really slow path.
$type = TypeParser::parseType($type, false);
}
if ($type[0] == TypeParser::TYPE_SCALAR) {
if (isset($this->typeHandlers[$type[1]])) {
if ($type instanceof ScalarType) {
if (isset($this->typeHandlers[$type->typeName])) {
// Assumption: if there's a type handler for this type string, then it's the right thing to use.
return array(TypeParser::TYPE_SCALAR, $type[1], $this->typeHandlers[$type[1]]);
$type->jsonType = $this->typeHandlers[$type->typeName];
} else {
return array(TypeParser::TYPE_OBJECT, $type[1]);
$type = new ObjectType($type->typeName);
}
}
return $type;
}

/**
* @param $value
* @param $type
* @throws Exception\InvalidTypeException
* @throws Exception\JsonMarshallerException
* @throws \Exception
* @return mixed
*/
protected function _decodeKey($value, $type)
{
if (!isset($value)) {
throw new JsonMarshallerException("Key values cannot be null");
}
$typeData = $this->_parseType($type);
switch ($typeData[0]) {
case TypeParser::TYPE_SCALAR:
// Keys are always strings, however we will allow other types, and disable strict type checking.
return $this->_decodeValue($value, $typeData, false);
default:
throw new JsonMarshallerException("Keys must be of type int or string, not " . $type);
if ($typeData instanceof ScalarType) {
// Keys are always strings, however we will allow other types, and disable strict type checking.
return $this->_decodeValue($value, $typeData, false);
} else {
throw new JsonMarshallerException("Keys must be of type int or string, not " . $type);
}
}

/**
* @param $value
* @param $type
* @param $strict
* @return mixed
* @throws Exception\InvalidTypeException
* @throws Exception\JsonMarshallerException
* @throws \Exception
*/
protected function _decodeValue($value, $type, $strict)
{
if (!isset($value)) {
return null;
}
$typeData = $this->_parseType($type);
switch (array_shift($typeData)) {
case TypeParser::TYPE_OBJECT:
switch (true) {
case ($typeData instanceof ObjectType):
if (!is_array($value)) {
throw new InvalidTypeException($typeData[0], $value);
throw new InvalidTypeException($typeData->class, $value);
}
return $this->_decodeClass($value, $typeData[0], $strict);
return $this->_decodeClass($value, $typeData->class, $strict);
break;
/** @noinspection PhpMissingBreakStatementInspection */
case TypeParser::TYPE_LIST:
case TypeParser::TYPE_MAP:
list ($indexType, $elementType) = $typeData;
case ($typeData instanceof MapType):
$indexType = $typeData->keyType;
case ($typeData instanceof ListType):
if (!isset($indexType)) {
$indexType = new ScalarType("int");
}
$elementType = $typeData->valueType;
$result = array();
if (!is_array($value)) {
$value = array($value);
Expand All @@ -533,13 +562,10 @@ protected function _decodeValue($value, $type, $strict)
$result[$this->_decodeKey($key, $indexType)] = $this->_decodeValue($element, $elementType, $strict);
}
return $result;
break;
case ($typeData instanceof ScalarType):
return $typeData->jsonType->decodeValue($value, $this, $strict);
default:
/**
* @var $typeHandler Types\JsonType
*/
list ($typeName, $typeHandler) = $typeData;
return $typeHandler->decodeValue($value, $this, $strict);
throw new \Exception("Invalid config for type");
}

}
Expand All @@ -550,12 +576,10 @@ protected function _encodeKey($value, $type)
throw new JsonMarshallerException("Key values cannot be null");
}
$typeData = $this->_parseType($type);
switch (array_shift($typeData)) {
/** @noinspection PhpMissingBreakStatementInspection */
case TypeParser::TYPE_SCALAR:
return $this->_encodeValue($value, array(TypeParser::TYPE_SCALAR, "string"));
default:
throw new JsonMarshallerException("Keys must be of type int or string, not " . $type);
if ($typeData instanceof ScalarType) {
return $this->_encodeValue($value, new ScalarType("string"));
} else {
throw new JsonMarshallerException("Keys must be of type int or string, not " . $type);
}
}

Expand All @@ -572,43 +596,39 @@ protected function _encodeValue($value, $type, $typeInfo = null)
return json_encode(null);
}
$typeData = $this->_parseType($type);
switch (array_shift($typeData)) {
case TypeParser::TYPE_OBJECT:
switch (true) {
case ($typeData instanceof ObjectType):
if (!is_object($value)) {
throw new InvalidTypeException($typeData[0], $value);
throw new InvalidTypeException($typeData->class, $value);
}
return $this->_encodeObject($value, $typeInfo, $typeData[0]);
break;
case TypeParser::TYPE_LIST:
list ($indexType, $elementType) = $typeData;
return $this->_encodeObject($value, $typeInfo, $typeData->class);
case ($typeData instanceof MapType):
$indexType = $typeData->keyType;
$elementType = $typeData->valueType;
if (!is_array($value)) {
$value = array($value);
}
$elements = array();
foreach ($value as $element) {
$elements[] = $this->_encodeValue($element, $elementType);
foreach ($value as $key => $element) {
$elements[] = $this->_encodeKey($key, $indexType) . ': ' . $this->_encodeValue($element,
$elementType);
}
return '[' . implode(', ', $elements) . ']';
case TypeParser::TYPE_MAP:
list ($indexType, $elementType) = $typeData;
return '{' . implode(', ', $elements) . '}';
case ($typeData instanceof ListType):
$elementType = $typeData->valueType;
if (!is_array($value)) {
$value = array($value);
}
$elements = array();
foreach ($value as $key => $element) {
$elements[] = $this->_encodeKey($key, $indexType) . ': ' . $this->_encodeValue($element,
$elementType);
foreach ($value as $element) {
$elements[] = $this->_encodeValue($element, $elementType);
}
return '{' . implode(', ', $elements) . '}';
return '[' . implode(', ', $elements) . ']';
case ($typeData instanceof ScalarType):
return $typeData->jsonType->encodeValue($value, $this);
default:
/**
* @var $typeHandler Types\JsonType
*/
list ($typeName, $typeHandler) = $typeData;
return $typeHandler->encodeValue($value, $this);

throw new \Exception("Unable to work out what to do with type");
}

}


Expand Down
Loading

0 comments on commit 53ef9ba

Please sign in to comment.