PageRenderTime 52ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/application/third_party/Facebook/GraphNodes/GraphNodeFactory.php

https://gitlab.com/lbsastry/idn-api-php
PHP | 392 lines | 310 code | 14 blank | 68 comment | 6 complexity | 9561c111546196f2991b75878d35f9de MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright 2014 Facebook, Inc.
  4. *
  5. * You are hereby granted a non-exclusive, worldwide, royalty-free license to
  6. * use, copy, modify, and distribute this software in source code or binary
  7. * form for use in connection with the web services and APIs provided by
  8. * Facebook.
  9. *
  10. * As with any software that integrates with the Facebook platform, your use
  11. * of this software is subject to the Facebook Developer Principles and
  12. * Policies [http://developers.facebook.com/policy/]. This copyright notice
  13. * shall be included in all copies or substantial portions of the software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  18. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21. * DEALINGS IN THE SOFTWARE.
  22. *
  23. */
  24. namespace Facebook\GraphNodes;
  25. use Facebook\FacebookResponse;
  26. use Facebook\Exceptions\FacebookSDKException;
  27. /**
  28. * Class GraphNodeFactory
  29. *
  30. * @package Facebook
  31. *
  32. * ## Assumptions ##
  33. * GraphEdge - is ALWAYS a numeric array
  34. * GraphEdge - is ALWAYS an array of GraphNode types
  35. * GraphNode - is ALWAYS an associative array
  36. * GraphNode - MAY contain GraphNode's "recurrable"
  37. * GraphNode - MAY contain GraphEdge's "recurrable"
  38. * GraphNode - MAY contain DateTime's "primitives"
  39. * GraphNode - MAY contain string's "primitives"
  40. */
  41. class GraphNodeFactory
  42. {
  43. /**
  44. * @const string The base graph object class.
  45. */
  46. const BASE_GRAPH_NODE_CLASS = '\Facebook\GraphNodes\GraphNode';
  47. /**
  48. * @const string The base graph edge class.
  49. */
  50. const BASE_GRAPH_EDGE_CLASS = '\Facebook\GraphNodes\GraphEdge';
  51. /**
  52. * @const string The graph object prefix.
  53. */
  54. const BASE_GRAPH_OBJECT_PREFIX = '\Facebook\GraphNodes\\';
  55. /**
  56. * @var FacebookResponse The response entity from Graph.
  57. */
  58. protected $response;
  59. /**
  60. * @var array The decoded body of the FacebookResponse entity from Graph.
  61. */
  62. protected $decodedBody;
  63. /**
  64. * Init this Graph object.
  65. *
  66. * @param FacebookResponse $response The response entity from Graph.
  67. */
  68. public function __construct(FacebookResponse $response)
  69. {
  70. $this->response = $response;
  71. $this->decodedBody = $response->getDecodedBody();
  72. }
  73. /**
  74. * Tries to convert a FacebookResponse entity into a GraphNode.
  75. *
  76. * @param string|null $subclassName The GraphNode sub class to cast to.
  77. *
  78. * @return GraphNode
  79. *
  80. * @throws FacebookSDKException
  81. */
  82. public function makeGraphNode($subclassName = null)
  83. {
  84. $this->validateResponseAsArray();
  85. $this->validateResponseCastableAsGraphNode();
  86. return $this->castAsGraphNodeOrGraphEdge($this->decodedBody, $subclassName);
  87. }
  88. /**
  89. * Convenience method for creating a GraphAchievement collection.
  90. *
  91. * @return GraphAchievement
  92. *
  93. * @throws FacebookSDKException
  94. */
  95. public function makeGraphAchievement()
  96. {
  97. return $this->makeGraphNode(static::BASE_GRAPH_OBJECT_PREFIX . 'GraphAchievement');
  98. }
  99. /**
  100. * Convenience method for creating a GraphAlbum collection.
  101. *
  102. * @return GraphAlbum
  103. *
  104. * @throws FacebookSDKException
  105. */
  106. public function makeGraphAlbum()
  107. {
  108. return $this->makeGraphNode(static::BASE_GRAPH_OBJECT_PREFIX . 'GraphAlbum');
  109. }
  110. /**
  111. * Convenience method for creating a GraphPage collection.
  112. *
  113. * @return GraphPage
  114. *
  115. * @throws FacebookSDKException
  116. */
  117. public function makeGraphPage()
  118. {
  119. return $this->makeGraphNode(static::BASE_GRAPH_OBJECT_PREFIX . 'GraphPage');
  120. }
  121. /**
  122. * Convenience method for creating a GraphSessionInfo collection.
  123. *
  124. * @return GraphSessionInfo
  125. *
  126. * @throws FacebookSDKException
  127. */
  128. public function makeGraphSessionInfo()
  129. {
  130. return $this->makeGraphNode(static::BASE_GRAPH_OBJECT_PREFIX . 'GraphSessionInfo');
  131. }
  132. /**
  133. * Convenience method for creating a GraphUser collection.
  134. *
  135. * @return GraphUser
  136. *
  137. * @throws FacebookSDKException
  138. */
  139. public function makeGraphUser()
  140. {
  141. return $this->makeGraphNode(static::BASE_GRAPH_OBJECT_PREFIX . 'GraphUser');
  142. }
  143. /**
  144. * Convenience method for creating a GraphEvent collection.
  145. *
  146. * @return GraphEvent
  147. *
  148. * @throws FacebookSDKException
  149. */
  150. public function makeGraphEvent()
  151. {
  152. return $this->makeGraphNode(static::BASE_GRAPH_OBJECT_PREFIX . 'GraphEvent');
  153. }
  154. /**
  155. * Convenience method for creating a GraphGroup collection.
  156. *
  157. * @return GraphGroup
  158. *
  159. * @throws FacebookSDKException
  160. */
  161. public function makeGraphGroup()
  162. {
  163. return $this->makeGraphNode(static::BASE_GRAPH_OBJECT_PREFIX . 'GraphGroup');
  164. }
  165. /**
  166. * Tries to convert a FacebookResponse entity into a GraphEdge.
  167. *
  168. * @param string|null $subclassName The GraphNode sub class to cast the list items to.
  169. * @param boolean $auto_prefix Toggle to auto-prefix the subclass name.
  170. *
  171. * @return GraphEdge
  172. *
  173. * @throws FacebookSDKException
  174. */
  175. public function makeGraphEdge($subclassName = null, $auto_prefix = true)
  176. {
  177. $this->validateResponseAsArray();
  178. $this->validateResponseCastableAsGraphEdge();
  179. if ($subclassName && $auto_prefix) {
  180. $subclassName = static::BASE_GRAPH_OBJECT_PREFIX . $subclassName;
  181. }
  182. return $this->castAsGraphNodeOrGraphEdge($this->decodedBody, $subclassName);
  183. }
  184. /**
  185. * Validates the decoded body.
  186. *
  187. * @throws FacebookSDKException
  188. */
  189. public function validateResponseAsArray()
  190. {
  191. if (!is_array($this->decodedBody)) {
  192. throw new FacebookSDKException('Unable to get response from Graph as array.', 620);
  193. }
  194. }
  195. /**
  196. * Validates that the return data can be cast as a GraphNode.
  197. *
  198. * @throws FacebookSDKException
  199. */
  200. public function validateResponseCastableAsGraphNode()
  201. {
  202. if (isset($this->decodedBody['data']) && static::isCastableAsGraphEdge($this->decodedBody['data'])) {
  203. throw new FacebookSDKException(
  204. 'Unable to convert response from Graph to a GraphNode because the response looks like a GraphEdge. Try using GraphNodeFactory::makeGraphEdge() instead.',
  205. 620
  206. );
  207. }
  208. }
  209. /**
  210. * Validates that the return data can be cast as a GraphEdge.
  211. *
  212. * @throws FacebookSDKException
  213. */
  214. public function validateResponseCastableAsGraphEdge()
  215. {
  216. if (!(isset($this->decodedBody['data']) && static::isCastableAsGraphEdge($this->decodedBody['data']))) {
  217. throw new FacebookSDKException(
  218. 'Unable to convert response from Graph to a GraphEdge because the response does not look like a GraphEdge. Try using GraphNodeFactory::makeGraphNode() instead.',
  219. 620
  220. );
  221. }
  222. }
  223. /**
  224. * Safely instantiates a GraphNode of $subclassName.
  225. *
  226. * @param array $data The array of data to iterate over.
  227. * @param string|null $subclassName The subclass to cast this collection to.
  228. *
  229. * @return GraphNode
  230. *
  231. * @throws FacebookSDKException
  232. */
  233. public function safelyMakeGraphNode(array $data, $subclassName = null)
  234. {
  235. $subclassName = $subclassName ?: static::BASE_GRAPH_NODE_CLASS;
  236. static::validateSubclass($subclassName);
  237. // Remember the parent node ID
  238. $parentNodeId = isset($data['id']) ? $data['id'] : null;
  239. $items = [];
  240. foreach ($data as $k => $v) {
  241. // Array means could be recurable
  242. if (is_array($v)) {
  243. // Detect any smart-casting from the $graphObjectMap array.
  244. // This is always empty on the GraphNode collection, but subclasses can define
  245. // their own array of smart-casting types.
  246. $graphObjectMap = $subclassName::getObjectMap();
  247. $objectSubClass = isset($graphObjectMap[$k])
  248. ? $graphObjectMap[$k]
  249. : null;
  250. // Could be a GraphEdge or GraphNode
  251. $items[$k] = $this->castAsGraphNodeOrGraphEdge($v, $objectSubClass, $k, $parentNodeId);
  252. } else {
  253. $items[$k] = $v;
  254. }
  255. }
  256. return new $subclassName($items);
  257. }
  258. /**
  259. * Takes an array of values and determines how to cast each node.
  260. *
  261. * @param array $data The array of data to iterate over.
  262. * @param string|null $subclassName The subclass to cast this collection to.
  263. * @param string|null $parentKey The key of this data (Graph edge).
  264. * @param string|null $parentNodeId The parent Graph node ID.
  265. *
  266. * @return GraphNode|GraphEdge
  267. *
  268. * @throws FacebookSDKException
  269. */
  270. public function castAsGraphNodeOrGraphEdge(array $data, $subclassName = null, $parentKey = null, $parentNodeId = null)
  271. {
  272. if (isset($data['data'])) {
  273. // Create GraphEdge
  274. if (static::isCastableAsGraphEdge($data['data'])) {
  275. return $this->safelyMakeGraphEdge($data, $subclassName, $parentKey, $parentNodeId);
  276. }
  277. // Sometimes Graph is a weirdo and returns a GraphNode under the "data" key
  278. $data = $data['data'];
  279. }
  280. // Create GraphNode
  281. return $this->safelyMakeGraphNode($data, $subclassName);
  282. }
  283. /**
  284. * Return an array of GraphNode's.
  285. *
  286. * @param array $data The array of data to iterate over.
  287. * @param string|null $subclassName The GraphNode subclass to cast each item in the list to.
  288. * @param string|null $parentKey The key of this data (Graph edge).
  289. * @param string|null $parentNodeId The parent Graph node ID.
  290. *
  291. * @return GraphEdge
  292. *
  293. * @throws FacebookSDKException
  294. */
  295. public function safelyMakeGraphEdge(array $data, $subclassName = null, $parentKey = null, $parentNodeId = null)
  296. {
  297. if (!isset($data['data'])) {
  298. throw new FacebookSDKException('Cannot cast data to GraphEdge. Expected a "data" key.', 620);
  299. }
  300. $dataList = [];
  301. foreach ($data['data'] as $graphNode) {
  302. $dataList[] = $this->safelyMakeGraphNode($graphNode, $subclassName);
  303. }
  304. $metaData = $this->getMetaData($data);
  305. // We'll need to make an edge endpoint for this in case it's a GraphEdge (for cursor pagination)
  306. $parentGraphEdgeEndpoint = $parentNodeId && $parentKey ? '/' . $parentNodeId . '/' . $parentKey : null;
  307. $className = static::BASE_GRAPH_EDGE_CLASS;
  308. return new $className($this->response->getRequest(), $dataList, $metaData, $parentGraphEdgeEndpoint, $subclassName);
  309. }
  310. /**
  311. * Get the meta data from a list in a Graph response.
  312. *
  313. * @param array $data The Graph response.
  314. *
  315. * @return array
  316. */
  317. public function getMetaData(array $data)
  318. {
  319. unset($data['data']);
  320. return $data;
  321. }
  322. /**
  323. * Determines whether or not the data should be cast as a GraphEdge.
  324. *
  325. * @param array $data
  326. *
  327. * @return boolean
  328. */
  329. public static function isCastableAsGraphEdge(array $data)
  330. {
  331. if ($data === []) {
  332. return true;
  333. }
  334. // Checks for a sequential numeric array which would be a GraphEdge
  335. return array_keys($data) === range(0, count($data) - 1);
  336. }
  337. /**
  338. * Ensures that the subclass in question is valid.
  339. *
  340. * @param string $subclassName The GraphNode subclass to validate.
  341. *
  342. * @throws FacebookSDKException
  343. */
  344. public static function validateSubclass($subclassName)
  345. {
  346. if ($subclassName == static::BASE_GRAPH_NODE_CLASS || is_subclass_of($subclassName, static::BASE_GRAPH_NODE_CLASS)) {
  347. return;
  348. }
  349. throw new FacebookSDKException('The given subclass "' . $subclassName . '" is not valid. Cannot cast to an object that is not a GraphNode subclass.', 620);
  350. }
  351. }