StripeObject.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. <?php
  2. namespace Stripe;
  3. use ArrayAccess;
  4. use InvalidArgumentException;
  5. class StripeObject implements ArrayAccess, JsonSerializable
  6. {
  7. /**
  8. * @var Util\Set Attributes that should not be sent to the API because
  9. * they're not updatable (e.g. API key, ID).
  10. */
  11. public static $permanentAttributes;
  12. /**
  13. * @var Util\Set Attributes that are nested but still updatable from
  14. * the parent class's URL (e.g. metadata).
  15. */
  16. public static $nestedUpdatableAttributes;
  17. public static function init()
  18. {
  19. self::$permanentAttributes = new Util\Set(array('_opts', 'id'));
  20. self::$nestedUpdatableAttributes = new Util\Set(array(
  21. 'metadata', 'legal_entity', 'address', 'dob', 'transfer_schedule', 'verification',
  22. 'tos_acceptance', 'personal_address',
  23. // will make the array into an AttachedObject: weird, but works for now
  24. 'additional_owners', 0, 1, 2, 3, 4 // Max 3, but leave the 4th so errors work properly
  25. ));
  26. }
  27. protected $_opts;
  28. protected $_values;
  29. protected $_unsavedValues;
  30. protected $_transientValues;
  31. protected $_retrieveOptions;
  32. public function __construct($id = null, $opts = null)
  33. {
  34. $this->_opts = $opts ? $opts : new Util\RequestOptions();
  35. $this->_values = array();
  36. $this->_unsavedValues = new Util\Set();
  37. $this->_transientValues = new Util\Set();
  38. $this->_retrieveOptions = array();
  39. if (is_array($id)) {
  40. foreach ($id as $key => $value) {
  41. if ($key != 'id') {
  42. $this->_retrieveOptions[$key] = $value;
  43. }
  44. }
  45. $id = $id['id'];
  46. }
  47. if ($id !== null) {
  48. $this->id = $id;
  49. }
  50. }
  51. // Standard accessor magic methods
  52. public function __set($k, $v)
  53. {
  54. if ($v === "") {
  55. throw new InvalidArgumentException(
  56. 'You cannot set \''.$k.'\'to an empty string. '
  57. .'We interpret empty strings as NULL in requests. '
  58. .'You may set obj->'.$k.' = NULL to delete the property'
  59. );
  60. }
  61. if (self::$nestedUpdatableAttributes->includes($k)
  62. && isset($this->$k) && is_array($v)) {
  63. $this->$k->replaceWith($v);
  64. } else {
  65. // TODO: may want to clear from $_transientValues (Won't be user-visible).
  66. $this->_values[$k] = $v;
  67. }
  68. if (!self::$permanentAttributes->includes($k)) {
  69. $this->_unsavedValues->add($k);
  70. }
  71. }
  72. public function __isset($k)
  73. {
  74. return isset($this->_values[$k]);
  75. }
  76. public function __unset($k)
  77. {
  78. unset($this->_values[$k]);
  79. $this->_transientValues->add($k);
  80. $this->_unsavedValues->discard($k);
  81. }
  82. public function &__get($k)
  83. {
  84. // function should return a reference, using $nullval to return a reference to null
  85. $nullval = null;
  86. if (array_key_exists($k, $this->_values)) {
  87. return $this->_values[$k];
  88. } else if ($this->_transientValues->includes($k)) {
  89. $class = get_class($this);
  90. $attrs = join(', ', array_keys($this->_values));
  91. $message = "Stripe Notice: Undefined property of $class instance: $k. "
  92. . "HINT: The $k attribute was set in the past, however. "
  93. . "It was then wiped when refreshing the object "
  94. . "with the result returned by Stripe's API, "
  95. . "probably as a result of a save(). The attributes currently "
  96. . "available on this object are: $attrs";
  97. error_log($message);
  98. return $nullval;
  99. } else {
  100. $class = get_class($this);
  101. error_log("Stripe Notice: Undefined property of $class instance: $k");
  102. return $nullval;
  103. }
  104. }
  105. // ArrayAccess methods
  106. public function offsetSet($k, $v)
  107. {
  108. $this->$k = $v;
  109. }
  110. public function offsetExists($k)
  111. {
  112. return array_key_exists($k, $this->_values);
  113. }
  114. public function offsetUnset($k)
  115. {
  116. unset($this->$k);
  117. }
  118. public function offsetGet($k)
  119. {
  120. return array_key_exists($k, $this->_values) ? $this->_values[$k] : null;
  121. }
  122. public function keys()
  123. {
  124. return array_keys($this->_values);
  125. }
  126. /**
  127. * This unfortunately needs to be public to be used in Util\Util
  128. *
  129. * @param array $values
  130. * @param array $opts
  131. *
  132. * @return StripeObject The object constructed from the given values.
  133. */
  134. public static function constructFrom($values, $opts)
  135. {
  136. $obj = new static(isset($values['id']) ? $values['id'] : null);
  137. $obj->refreshFrom($values, $opts);
  138. return $obj;
  139. }
  140. /**
  141. * Refreshes this object using the provided values.
  142. *
  143. * @param array $values
  144. * @param array $opts
  145. * @param boolean $partial Defaults to false.
  146. */
  147. public function refreshFrom($values, $opts, $partial = false)
  148. {
  149. $this->_opts = $opts;
  150. // Wipe old state before setting new. This is useful for e.g. updating a
  151. // customer, where there is no persistent card parameter. Mark those values
  152. // which don't persist as transient
  153. if ($partial) {
  154. $removed = new Util\Set();
  155. } else {
  156. $removed = array_diff(array_keys($this->_values), array_keys($values));
  157. }
  158. foreach ($removed as $k) {
  159. if (self::$permanentAttributes->includes($k)) {
  160. continue;
  161. }
  162. unset($this->$k);
  163. }
  164. foreach ($values as $k => $v) {
  165. if (self::$permanentAttributes->includes($k) && isset($this[$k])) {
  166. continue;
  167. }
  168. if (self::$nestedUpdatableAttributes->includes($k) && is_array($v)) {
  169. $this->_values[$k] = AttachedObject::constructFrom($v, $opts);
  170. } else {
  171. $this->_values[$k] = Util\Util::convertToStripeObject($v, $opts);
  172. }
  173. $this->_transientValues->discard($k);
  174. $this->_unsavedValues->discard($k);
  175. }
  176. }
  177. /**
  178. * @return array A recursive mapping of attributes to values for this object,
  179. * including the proper value for deleted attributes.
  180. */
  181. public function serializeParameters()
  182. {
  183. $params = array();
  184. if ($this->_unsavedValues) {
  185. foreach ($this->_unsavedValues->toArray() as $k) {
  186. $v = $this->$k;
  187. if ($v === null) {
  188. $v = '';
  189. }
  190. $params[$k] = $v;
  191. }
  192. }
  193. // Get nested updates.
  194. foreach (self::$nestedUpdatableAttributes->toArray() as $property) {
  195. if (isset($this->$property)) {
  196. if ($this->$property instanceof StripeObject) {
  197. $serialized = $this->$property->serializeParameters();
  198. if ($serialized) {
  199. $params[$property] = $serialized;
  200. }
  201. }
  202. }
  203. }
  204. return $params;
  205. }
  206. public function jsonSerialize()
  207. {
  208. return $this->__toArray(true);
  209. }
  210. public function __toJSON()
  211. {
  212. if (defined('JSON_PRETTY_PRINT')) {
  213. return json_encode($this->__toArray(true), JSON_PRETTY_PRINT);
  214. } else {
  215. return json_encode($this->__toArray(true));
  216. }
  217. }
  218. public function __toString()
  219. {
  220. $class = get_class($this);
  221. return $class . ' JSON: ' . $this->__toJSON();
  222. }
  223. public function __toArray($recursive = false)
  224. {
  225. if ($recursive) {
  226. return Util\Util::convertStripeObjectToArray($this->_values);
  227. } else {
  228. return $this->_values;
  229. }
  230. }
  231. }
  232. StripeObject::init();