ClassMetadata.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <?php
  2. /*
  3. * Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. namespace JMS\SerializerBundle\Metadata;
  18. use JMS\SerializerBundle\Exception\InvalidArgumentException;
  19. use Metadata\MergeableInterface;
  20. use Metadata\MethodMetadata;
  21. use Metadata\MergeableClassMetadata;
  22. use Metadata\PropertyMetadata as BasePropertyMetadata;
  23. /**
  24. * Class Metadata used to customize the serialization process.
  25. *
  26. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  27. */
  28. class ClassMetadata extends MergeableClassMetadata
  29. {
  30. const ACCESSOR_ORDER_UNDEFINED = 'undefined';
  31. const ACCESSOR_ORDER_ALPHABETICAL = 'alphabetical';
  32. const ACCESSOR_ORDER_CUSTOM = 'custom';
  33. public $preSerializeMethods = array();
  34. public $postSerializeMethods = array();
  35. public $postDeserializeMethods = array();
  36. public $virtualPropertyMethods = array();
  37. public $xmlRootName;
  38. public $accessorOrder;
  39. public $customOrder;
  40. /**
  41. * Sets the order of properties in the class.
  42. *
  43. * @param string $order
  44. * @param array $customOrder
  45. */
  46. public function setAccessorOrder($order, array $customOrder = array())
  47. {
  48. if (!in_array($order, array(self::ACCESSOR_ORDER_UNDEFINED, self::ACCESSOR_ORDER_ALPHABETICAL, self::ACCESSOR_ORDER_CUSTOM), true)) {
  49. throw new \InvalidArgumentException(sprintf('The accessor order "%s" is invalid.', $order));
  50. }
  51. foreach ($customOrder as $name) {
  52. if (!is_string($name)) {
  53. throw new \InvalidArgumentException(sprintf('$customOrder is expected to be a list of strings, but got element of value %s.', json_encode($name)));
  54. }
  55. }
  56. $this->accessorOrder = $order;
  57. $this->customOrder = array_flip($customOrder);
  58. $this->sortProperties();
  59. }
  60. public function addPropertyMetadata(BasePropertyMetadata $metadata)
  61. {
  62. parent::addPropertyMetadata($metadata);
  63. $this->sortProperties();
  64. }
  65. public function addPreSerializeMethod(MethodMetadata $method)
  66. {
  67. $this->preSerializeMethods[] = $method;
  68. }
  69. public function addPostSerializeMethod(MethodMetadata $method)
  70. {
  71. $this->postSerializeMethods[] = $method;
  72. }
  73. public function addPostDeserializeMethod(MethodMetadata $method)
  74. {
  75. $this->postDeserializeMethods[] = $method;
  76. }
  77. public function addVirtualPropertyMethod(MethodMetadata $method, $field)
  78. {
  79. $this->virtualPropertyMethods[$field] = $method;
  80. }
  81. public function merge(MergeableInterface $object)
  82. {
  83. if (!$object instanceof ClassMetadata) {
  84. throw new InvalidArgumentException('$object must be an instance of ClassMetadata.');
  85. }
  86. parent::merge($object);
  87. $this->preSerializeMethods = array_merge($this->preSerializeMethods, $object->preSerializeMethods);
  88. $this->postSerializeMethods = array_merge($this->postSerializeMethods, $object->postSerializeMethods);
  89. $this->postDeserializeMethods = array_merge($this->postDeserializeMethods, $object->postDeserializeMethods);
  90. $this->xmlRootName = $object->xmlRootName;
  91. if ($object->accessorOrder) {
  92. $this->accessorOrder = $object->accessorOrder;
  93. $this->customOrder = $object->customOrder;
  94. }
  95. $this->sortProperties();
  96. }
  97. public function serialize()
  98. {
  99. $this->sortProperties();
  100. return serialize(array(
  101. $this->preSerializeMethods,
  102. $this->postSerializeMethods,
  103. $this->postDeserializeMethods,
  104. $this->virtualPropertyMethods,
  105. $this->xmlRootName,
  106. $this->accessorOrder,
  107. $this->customOrder,
  108. parent::serialize(),
  109. ));
  110. }
  111. public function unserialize($str)
  112. {
  113. list(
  114. $this->preSerializeMethods,
  115. $this->postSerializeMethods,
  116. $this->postDeserializeMethods,
  117. $this->virtualPropertyMethods,
  118. $this->xmlRootName,
  119. $this->accessorOrder,
  120. $this->customOrder,
  121. $parentStr
  122. ) = unserialize($str);
  123. parent::unserialize($parentStr);
  124. }
  125. private function sortProperties()
  126. {
  127. switch ($this->accessorOrder) {
  128. case self::ACCESSOR_ORDER_ALPHABETICAL:
  129. ksort($this->propertyMetadata);
  130. break;
  131. case self::ACCESSOR_ORDER_CUSTOM:
  132. $order = $this->customOrder;
  133. uksort($this->propertyMetadata, function($a, $b) use ($order) {
  134. $existsA = isset($order[$a]);
  135. $existsB = isset($order[$b]);
  136. if (!$existsA && !$existsB) {
  137. return 0;
  138. }
  139. if (!$existsA) {
  140. return 1;
  141. }
  142. if (!$existsB) {
  143. return -1;
  144. }
  145. return $order[$a] < $order[$b] ? -1 : 1;
  146. });
  147. break;
  148. }
  149. }
  150. }