PageRenderTime 62ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Resource/Api.php

https://github.com/andyburton/Sonic-Framework
PHP | 864 lines | 303 code | 337 blank | 224 comment | 43 complexity | 40f0922128c5716347176c2d508d8512 MD5 | raw file
  1. <?php
  2. // Define namespace
  3. namespace Sonic\Resource;
  4. // Start Api Class
  5. class Api
  6. {
  7. /**
  8. * API Action
  9. * @var string
  10. */
  11. protected $action = NULL;
  12. /**
  13. * API Result
  14. * @var array
  15. */
  16. protected $result = array ();
  17. /**
  18. * Method Arguments
  19. * @var array
  20. */
  21. protected $args = array ();
  22. /**
  23. * Request Type
  24. * @var string
  25. */
  26. protected $requestType = 'post';
  27. /**
  28. * Return Format
  29. * @var string
  30. */
  31. protected $returnType = 'json';
  32. /**
  33. * API Modules
  34. * @var array
  35. */
  36. protected $modules = array ();
  37. /**
  38. * Root node name for XML
  39. * @var string
  40. */
  41. protected $rootNode = 'api';
  42. /**
  43. * API method limitations
  44. * Defaults to all modules/methods accessible.
  45. * This allows you to determine which modules and methods are accessible through the API.
  46. * The 'all' key is either TRUE or FALSE, and overrides any other limits.
  47. * This needs to be removed if you have set specific module limitations.
  48. * Module limits can be defined by setting them as a key, and method permissions as a sub array.
  49. * Method values can be TRUE or FALSE to determine where they can be called or not.
  50. * The module value can also be TRUE or FALSE to set a limit for the entire module is no methods are set.
  51. * @var array
  52. */
  53. protected $actionLimit = array ('all' => TRUE);
  54. /**
  55. * Deal with an api exception
  56. * @param Exception $exception Exception
  57. * @return void
  58. */
  59. public function Exception ($exception)
  60. {
  61. // Clear output buffers
  62. while (ob_get_level ())
  63. {
  64. ob_end_clean ();
  65. }
  66. // Set error
  67. $this->result = array (
  68. 'status' => 'fail',
  69. 'message' => 'exception',
  70. 'exception' => array (
  71. 'file' => $exception->getFile (),
  72. 'line' => $exception->getLine (),
  73. 'message' => $exception->getMessage ()
  74. )
  75. );
  76. // Auditlog
  77. $auditlog = \Sonic\Sonic::getResource ('auditlog');
  78. if ($auditlog instanceof Audit\Log)
  79. {
  80. $auditlog::_Log ($this->action, 7, $this->args, $this->result['exception']);
  81. }
  82. // Display
  83. $this->Display ();
  84. }
  85. /**
  86. * Wrapper function to complete an API request
  87. * Set request type, action arguments and response type.
  88. * Call the action and then log.
  89. * @param string $action Action
  90. * @return void
  91. */
  92. public function callAction ($action = FALSE)
  93. {
  94. // Set the request type
  95. $this->setRequestType ();
  96. // Set arguments
  97. $this->setArguments ();
  98. // Set the return type
  99. $this->setReturnType ();
  100. // Call action
  101. $this->Action ($action);
  102. }
  103. /**
  104. * Set the API request type
  105. * @param string $type Request Type
  106. * @return void
  107. */
  108. public function setRequestType ($type = FALSE)
  109. {
  110. // If there is a request argument
  111. if ($type !== FALSE)
  112. {
  113. // Set it
  114. $this->requestType = $type;
  115. }
  116. // Else if there is a request format URL variable
  117. else if (isset ($_GET['request_type']))
  118. {
  119. // Set it
  120. $this->requestType = $_GET['request_type'];
  121. }
  122. }
  123. /**
  124. * Set the API return type
  125. * @param string $strType Return Type
  126. * @return void
  127. */
  128. public function setReturnType ($type = FALSE)
  129. {
  130. // If there is a return argument
  131. if ($type !== FALSE)
  132. {
  133. // Set it
  134. $this->returnType = $type;
  135. }
  136. // Else if there is a return type in the arguments
  137. else if (isset ($this->args['return_type']))
  138. {
  139. // Set it
  140. $this->returnType = $this->args['return_type'];
  141. }
  142. }
  143. /**
  144. * Set the API request arguments
  145. * @return void
  146. */
  147. public function setArguments ()
  148. {
  149. // Switch the request type
  150. switch ($this->requestType)
  151. {
  152. // GET
  153. case 'get':
  154. $this->setArguments_GET ();
  155. break;
  156. // POST
  157. case 'post':
  158. default:
  159. $this->setArguments_POST ();
  160. break;
  161. }
  162. }
  163. /**
  164. * Set the API arguments from a POST request
  165. * @return void
  166. */
  167. public function setArguments_POST ()
  168. {
  169. $this->args = $_POST;
  170. }
  171. /**
  172. * Set the API arguments from a GET request
  173. * @return void
  174. */
  175. public function setArguments_GET ()
  176. {
  177. $this->args = $_GET;
  178. }
  179. /**
  180. * Call an API action
  181. * @param string $strAction Action
  182. * @return mixed
  183. */
  184. public function Action ($strAction = FALSE)
  185. {
  186. // If an action argument is set
  187. if ($strAction)
  188. {
  189. // Set it
  190. $this->action = $strAction;
  191. }
  192. // Else if a method argument is set
  193. else if (isset ($this->args['method']))
  194. {
  195. // Set it
  196. $this->action = $this->args['method'];
  197. }
  198. // Auditlog
  199. $auditlog = \Sonic\Sonic::getResource ('auditlog');
  200. $logType = 2; // Default to success
  201. $logParams = FALSE;
  202. $logResponse = FALSE;
  203. // Explode action
  204. $arrAction = explode ('.', $this->action);
  205. // If there are 2 or more items then there is a module and permission
  206. if (count ($arrAction) >= 2)
  207. {
  208. // Set the module and method names
  209. $strMethod = array_pop ($arrAction);
  210. $strModule = implode ('.', $arrAction);
  211. // Get the module object
  212. $objModule = $this->getModule ($strModule);
  213. // If the module was loaded
  214. if ($objModule)
  215. {
  216. // If the method exists
  217. if ($objModule->checkMethod ($strMethod))
  218. {
  219. // Check the method limitation
  220. if ($this->checkLimitation ($strModule, $strMethod))
  221. {
  222. // Call the method
  223. $arrReturn = $objModule->callMethod ($this, $strModule, $strMethod, $this->args);
  224. // If the return is ok
  225. if ($arrReturn['status'] == 'ok')
  226. {
  227. // Set as success
  228. $this->result['status'] = 'ok';
  229. // Remove any result status
  230. unset ($arrReturn['status']);
  231. // Add return
  232. if ($arrReturn)
  233. {
  234. $this->result[$strMethod] = $arrReturn;
  235. }
  236. // Auditlog
  237. $logType = 2;
  238. // $logParams = $this->args;
  239. // $logResponse = $this->result;
  240. }
  241. else
  242. {
  243. // Set any results
  244. foreach ($arrReturn as $strKey => $strVal)
  245. {
  246. $this->result[$strKey] = $strVal;
  247. }
  248. // Auditlog
  249. $logType = 3;
  250. $logParams = $this->args;
  251. $logResponse = @$this->result['message'];
  252. }
  253. }
  254. // Else the module or method is limited
  255. else
  256. {
  257. // Set error
  258. $this->result['status'] = 'fail';
  259. $this->result['message'] = 'method access limited';
  260. // Set auditlog type
  261. $logType = 11;
  262. }
  263. }
  264. // Else the method was not loaded
  265. else
  266. {
  267. // Set error
  268. $this->result['status'] = 'fail';
  269. $this->result['message'] = 'invalid method';
  270. // Set auditlog type
  271. $logType = 10;
  272. }
  273. }
  274. // Else the module was not loaded
  275. else
  276. {
  277. // Set error
  278. $this->result['status'] = 'fail';
  279. $this->result['message'] = 'invalid module';
  280. // Set auditlog type
  281. $logType = 9;
  282. }
  283. }
  284. // Else invalid action format
  285. else
  286. {
  287. // Set error
  288. $this->result['status'] = 'fail';
  289. $this->result['message'] = 'invalid action';
  290. // Set auditlog type
  291. $logType = 8;
  292. }
  293. // Set the request and response types and the action
  294. $this->result['method'] = $this->action;
  295. $this->result['request_type'] = $this->requestType;
  296. $this->result['return_type'] = $this->returnType;
  297. // Auditlog
  298. if ($auditlog instanceof Audit\Log)
  299. {
  300. $auditlog::_Log ($this->action, $logType, $logParams, $logResponse);
  301. }
  302. // Return TRUE
  303. return TRUE;
  304. }
  305. /**
  306. * Return an API module object if it exists
  307. * @param string $module Module name
  308. * @return Api\Module
  309. */
  310. public function getModule ($module)
  311. {
  312. // Load modules
  313. $this->getModules ();
  314. // Loop through the modules
  315. foreach ($this->modules as $obj)
  316. {
  317. // If the module names match and the class exists
  318. if (strtolower ($obj->get ('name')) == $module && class_exists ($obj->getModuleClass ()))
  319. {
  320. // Return the module
  321. return $obj;
  322. }
  323. }
  324. // Return FALSE
  325. return FALSE;
  326. }
  327. /**
  328. * Load the API modules
  329. * @return array
  330. */
  331. public function getModules ()
  332. {
  333. // If the modules have not been loaded
  334. if (!$this->modules)
  335. {
  336. // Set the module classs
  337. $class = get_called_class () . '\Module';
  338. // Load the modules
  339. $this->modules = $class::_getObjects ();
  340. }
  341. // Return the modules
  342. return $this->modules;
  343. }
  344. /**
  345. * Check whether a module method is limited
  346. * @param string $module Module Name
  347. * @param string $method Method Name
  348. * @return param
  349. */
  350. public function checkLimitation ($module, $method)
  351. {
  352. // If no methods are defined or all is denied
  353. if (!$this->actionLimit || !Parser::_ak ($this->actionLimit, 'all', TRUE))
  354. {
  355. // Deny all
  356. return FALSE;
  357. }
  358. // If all methods are enabled
  359. if (Parser::_ak ($this->actionLimit, 'all', FALSE))
  360. {
  361. // Allow all
  362. return TRUE;
  363. }
  364. // If the module method is allowed
  365. if (Parser::_ak ($this->actionLimit, array ($module, $method), FALSE))
  366. {
  367. // Allow
  368. return TRUE;
  369. }
  370. // If the module is allowed
  371. if (Parser::_ak ($this->actionLimit, $module, FALSE) === TRUE)
  372. {
  373. // Allow
  374. return TRUE;
  375. }
  376. // Deny
  377. return FALSE;
  378. }
  379. /**
  380. * Display the API result
  381. * @return string
  382. */
  383. public function Display ()
  384. {
  385. // Send headers
  386. header ("Expires: Sun, 23 Nov 1984 03:13:37 GMT");
  387. header ("Last-Modified: " . gmdate ("D, d M Y H:i:s") . " GMT");
  388. header ("Cache-Control: no-store, no-cache, must-revalidate");
  389. header ("Cache-Control: post-check=0, pre-check=0", FALSE);
  390. header ("Pragma: no-cache");
  391. // Remove auth error if the user is authenticated
  392. // This is created during the user init even if they are later authenticated
  393. if (Parser::_ak ($this->result, 'authenticated', FALSE) !== 0)
  394. {
  395. unset ($this->result['auth_error']);
  396. }
  397. //ob_start ('ob_gzhandler');
  398. // Return
  399. switch ($this->returnType)
  400. {
  401. // XML
  402. case 'xml':
  403. header ('Content-Type: application/xml');
  404. echo $this->outputXML ();
  405. break;
  406. // Serialized
  407. case 'serialized':
  408. header ('Content-Type: application/vnd.php.serialized');
  409. echo $this->outputSerialized ();
  410. break;
  411. // ExtJS
  412. case 'extjs':
  413. // ExtJS must return success status, so convert the status variable
  414. $this->result['success'] = $this->result['status'] == 'ok';
  415. unset ($this->result['status']);
  416. header ('Content-Type: application/json');
  417. echo $this->outputJSON ();
  418. break;
  419. // Special case for file uploads as chrome cannot handle JSON content type responses
  420. case 'fileupload':
  421. // ExtJS must return success status, so convert the status variable
  422. $this->result['success'] = $this->result['status'] == 'ok';
  423. unset ($this->result['status']);
  424. header ('Content-Type: text/html');
  425. echo $this->outputJSON ();
  426. break;
  427. // JSON
  428. case 'json':
  429. default:
  430. header ('Content-Type: application/json');
  431. echo $this->outputJSON ();
  432. break;
  433. }
  434. //ob_end_flush ();
  435. }
  436. /**
  437. * Return JSON encoded result
  438. * @return string
  439. */
  440. public function outputJSON ()
  441. {
  442. return json_encode ($this->result);
  443. }
  444. /**
  445. * Return XML encoded result
  446. * @return string
  447. */
  448. public function outputXML ()
  449. {
  450. // Create DOMDocument
  451. $doc = new \DOMDocument ('1.0', 'UTF-8');
  452. // Generate XML
  453. $xml = $this->generateXML ($doc);
  454. // Append XML
  455. $doc->appendChild ($xml);
  456. // Return XML
  457. return $doc->saveXML ();
  458. }
  459. /**
  460. * Generate XML from result
  461. * @param object $doc DOMDocument Object
  462. * @param array $arr Array element to convert
  463. * @param object $xml DOMElement Parent Object
  464. * @return \DomElement
  465. */
  466. public function generateXML (&$doc, $arr = FALSE, $xml = FALSE)
  467. {
  468. // If there is DOMElement reate the root element
  469. if ($xml === FALSE)
  470. {
  471. $xml = $doc->createElement (strtolower ($this->rootNode));
  472. }
  473. // If there is no element set to the result
  474. if ($arr === FALSE)
  475. {
  476. $arr = $this->result;
  477. }
  478. // For each element in the element types
  479. foreach ($arr as $key => $val)
  480. {
  481. // If the element is an object and the key is a number
  482. if (is_object ($val) && is_numeric ($key))
  483. {
  484. // Set key to class name
  485. $key = get_class ($val);
  486. }
  487. // Else if there is an array key 'class'
  488. else if (is_array ($val) && array_key_exists ('class', $val))
  489. {
  490. // Set key to class name
  491. $key = $val['class'];
  492. // Remove class key
  493. unset ($val['class']);
  494. }
  495. // Else if the key is a number
  496. else if (is_numeric ($key))
  497. {
  498. // Set key to string
  499. $key = 'result_' . $key;
  500. }
  501. // Create a new element
  502. $element = $doc->createElement (strtolower ($key));
  503. // If the value is an array generate XML
  504. if (is_array ($val))
  505. {
  506. $element = $this->generateXML ($doc, $val, $element);
  507. }
  508. // Else set the element value
  509. else
  510. {
  511. $element->nodeValue = htmlentities ($val);
  512. }
  513. // Append element
  514. $xml->appendChild ($element);
  515. }
  516. return $xml;
  517. }
  518. /**
  519. * Return serialized result
  520. * @return string
  521. */
  522. public function outputSerialized ()
  523. {
  524. return serialize ($this->result);
  525. }
  526. }
  527. // End Api Class