PageRenderTime 27ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Server/SWXPHP/2.00/php/swx.php

http://swx-format.googlecode.com/
PHP | 438 lines | 250 code | 55 blank | 133 comment | 34 complexity | b85b72f2c9b20d87d646bfcd08da2b13 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. <?php
  2. /**
  3. * PHP implementation of SWX RPC.
  4. *
  5. * This script acts as the endpoint (gateway) for SWX RPC calls.
  6. * It returns a SWX SWF files
  7. *
  8. * @author Aral Balkan
  9. * @copyright (c) 2007 Aral Balkan
  10. * @link http://aralbalkan.com
  11. * @link http://swxformat.org
  12. *
  13. * Licensed under the Creative Commons GNU GPL License.
  14. *
  15. * This gateway handles incoming SWX requests. It instantiates
  16. * the requested service class and calls the requested method
  17. * on the service class instance, passing to it the data
  18. * argument (if any).
  19. *
  20. **/
  21. // Output buffering if used to ensure a clean SWF output
  22. @ob_start();
  23. // Simple profiling
  24. function microtime_float()
  25. {
  26. list($usec, $sec) = explode(" ", microtime());
  27. return ((float)$usec + (float)$sec);
  28. }
  29. // Save start time
  30. $startTime = microtime_float();
  31. // Define E_STRICT if not defined so we don't get errors on PHP 4
  32. if (!defined('E_STRICT')) define('E_STRICT', 2048);
  33. define('LETTERS_AND_NUMBERS_ONLY', '/[a-zA-Z0-9_]/');
  34. $class = '';
  35. $method = '';
  36. $url = '';
  37. $data = 'array()';
  38. $debug = false;
  39. // Error handling
  40. function errorHandler($errorNum, $errorStr, $errorFile, $errorLine)
  41. {
  42. $errorMsg = "Error $errorNum: $errorStr in $errorFile, line $errorLine.";
  43. $GLOBALS['swxLastErrorMessage'] = $errorMsg;
  44. // Display the error message in the PHP error log
  45. error_log($errorMsg);
  46. $errorObj = array('error' => TRUE, 'code' => $errorNum, 'message' => $errorMsg);
  47. //if ($errorNum == E_ERROR || $errorNum == E_WARNING || $errorNum = E_USER_ERROR)
  48. // Error num check replaced by code from http://drupal.org/node/11772#comment-18383.
  49. // This stops PHP5 strict errors from failing a call (e.g., deprecated calls, etc.)
  50. //if (($errorNum & (E_ALL & E_STRICT) ^ (E_NOTICE & E_STRICT)) || $errorNum = E_USER_ERROR)
  51. if ($errorNum != E_STRICT && $errorNum != E_NOTICE)
  52. {
  53. // On errors and warnings, stop execution and return the
  54. // error message to the client. This is a far better
  55. // alternative to failing silently.
  56. returnError($errorObj);
  57. }
  58. }
  59. // Error handling (unfortunately has to be global to support PHP 4)
  60. set_error_handler('errorHandler');
  61. // Load the configuration info
  62. include('swx_config.php');
  63. // Turn on error reporting
  64. switch($errorReportingLevel) {
  65. case 0: error_reporting(0); break;
  66. case 1: error_reporting(E_ERROR | E_PARSE | E_COMPILE_ERROR); break;
  67. case 2: error_reporting(E_ERROR | E_PARSE | E_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING); break;
  68. case 3: error_reporting(E_ERROR | E_PARSE | E_WARNING | E_NOTICE | E_COMPILE_ERROR | E_COMPILE_WARNING); break;
  69. case 4: error_reporting(E_ALL); break;
  70. }
  71. // PHP 5 compatibility layer for PHP 4
  72. include('lib/str_split.php');
  73. // Load and instantiate the SWX Assembler
  74. $defaultFormat = 'as2';
  75. if (isset($_POST['format']))
  76. $defaultFormat = $_POST['format'];
  77. elseif (isset($_GET['format']))
  78. $defaultFormat = $_GET['format'];
  79. if ($defaultFormat=='as2')
  80. {
  81. include('core/swx/SwxAS2Assembler.php');
  82. $swxAssembler = new SwxAS2Assembler();
  83. }
  84. else
  85. {
  86. include('core/swx/SwxAS3Assembler.php');
  87. $swxAssembler = new SwxAS3Assembler();
  88. }
  89. // Global configuration information
  90. global $swx;
  91. $swx = array();
  92. // Change to the service folder from here on.
  93. // This works exactly like Amfphp to make sure that Amfphp and SWX
  94. // services are compatible.
  95. chdir($servicesPath);
  96. /**
  97. * Returns a SWX with the passed error message in the result object.
  98. *
  99. * @param (str) The error message to return to the client.
  100. *
  101. * @return void (exits)
  102. * @author Aral Balkan
  103. **/
  104. function returnError($errorObj)
  105. {
  106. global $swxAssembler, $debug;
  107. error_log($errorObj['message']);
  108. $swxAssembler->writeSwf($errorObj, $debug);
  109. exit();
  110. }
  111. /**
  112. * Reads parameters from the passed source.
  113. *
  114. * @param array Either $_GET or $_POST.
  115. * @return void
  116. * @author Aral Balkan
  117. **/
  118. function getParameters($source)
  119. {
  120. global $class, $method, $data, $debug, $swxAssembler, $isParameterObject, $url;
  121. // Debug mode?
  122. if (array_key_exists('debug', $source))
  123. {
  124. $debug = ($source['debug'] === 'true');
  125. }
  126. else
  127. {
  128. // If no debug parameter is passed, debug defaults to false.
  129. $debug = false;
  130. }
  131. // Were any arguments passed?
  132. if (isset($source['args'])) // if (array_key_exists('args', $source))
  133. {
  134. // error_log('[SWX] INFO Arguments: ' . $source['args']);
  135. $data = $source['args'];
  136. }
  137. // Get the class name
  138. if (isset($source['serviceClass'])) //if (array_key_exists('serviceClass', $source))
  139. {
  140. $class = $source['serviceClass'];
  141. }
  142. else
  143. {
  144. // Error: Service class argument is missing.
  145. trigger_error('The \'serviceClass\' argument is missing (no class name was supplied)', E_USER_ERROR);
  146. }
  147. // Check if service class is null or undefined
  148. if ($class === "null") trigger_error('The \'serviceClass\' argument is null.', E_USER_ERROR);
  149. if ($class === "undefined") trigger_error('The \'serviceClass\' argument is undefined.', E_USER_ERROR);
  150. // Get the method name
  151. if (isset($source['method'])) // if (array_key_exists('method', $source))
  152. {
  153. $method = $source['method'];
  154. }
  155. else
  156. {
  157. // Error: Method argument is missing.
  158. trigger_error('The \'method\' argument is missing (no method name was supplied)', E_USER_ERROR);
  159. }
  160. // Check if method is null or undefined
  161. if ($method === "null") trigger_error('The \'method\' argument is null.', E_USER_ERROR);
  162. if ($method === "undefined") trigger_error('The \'method\' argument is undefined.', E_USER_ERROR);
  163. // TODO: Implement as part of the new security
  164. // model in the next Beta.
  165. //
  166. // Get the url that we are being called from
  167. // (for cross-domain support)
  168. if (isset($source['url'])) //(array_key_exists('url', $source))
  169. {
  170. $url = urldecode($source['url']);
  171. // Firefox/Flash (at least, and tested only on a Mac), sends
  172. // file:/// (three slashses) in the URI and that fails the validation
  173. // so replacing that with two slashes instead.
  174. $url = str_replace('///', '//', $url);
  175. if (LOG_ALL) error_log('[SWX] INFO: SWX gateway called from '.$url);
  176. }
  177. else
  178. {
  179. error_log('[SWX] Warning: No referring URL received from Flash. Cross-domain will not be supported on this call regardless of allowDomain setting.');
  180. }
  181. }
  182. // Check if the className is supplied as a GET var. If so,
  183. // we'll use those. Using GET is useful for debugging.
  184. //if (isset($_GET['serviceClass'])) // (array_key_exists('serviceClass', $_GET))
  185. if ($_SERVER['REQUEST_METHOD'] === 'GET')
  186. {
  187. // GET
  188. //error_log('[SWX] INFO Using GET.');
  189. getParameters($_GET);
  190. }
  191. else
  192. {
  193. // POST
  194. //error_log('[SWX] INFO Using POST.');
  195. getParameters($_POST);
  196. }
  197. // Security: Check that only allowed characters are present in the URL.
  198. require_once('lib/Validate.php');
  199. $v = new Validate();
  200. /*
  201. $options = array
  202. (
  203. 'domain_check' => false,
  204. 'allow_schemes' => array
  205. (
  206. 'http', 'https', 'file'
  207. ),
  208. 'strict' => ''
  209. );
  210. */
  211. $urlValid = $v->uri($url);
  212. if ($urlValid != 1)
  213. {
  214. error_log('[SWX] Non-fatal error: URL is not valid. Cross-domain access will not work. ' . $url);
  215. }
  216. else
  217. {
  218. // URL is valid
  219. if (LOG_ALL) error_log('[SWX] INFO: The referring URL is valid.');
  220. }
  221. // Security: Check that only allowed characters are present
  222. // in the class and method names.
  223. $classNameDisallowedCharacters = preg_replace(LETTERS_AND_NUMBERS_ONLY, '', $class);
  224. $methodNameDisallowedCharacters = preg_replace(LETTERS_AND_NUMBERS_ONLY, '', $method);
  225. if ($classNameDisallowedCharacters !== '')
  226. {
  227. // Error: Invalid class name.
  228. trigger_error("The supplied class name ($class) is invalid (it must only contain letters, numbers, and underscores)", E_USER_ERROR);
  229. }
  230. if ($methodNameDisallowedCharacters !== '')
  231. {
  232. // Error: Invalid method name.
  233. trigger_error("The supplied method name ($method) is invalid (it must only contain letters, numbers, and underscores)", E_USER_ERROR);
  234. }
  235. // Load in the requested class
  236. $classToLoad = $class.'.php';
  237. if (is_file($classToLoad))
  238. {
  239. include($classToLoad);
  240. }
  241. else
  242. {
  243. // The requested class does not exist.
  244. trigger_error('Could not find a class named '.$class, E_USER_ERROR);
  245. }
  246. // Instantiate the requested service class and
  247. // call the requested method on it, capturing the return value.
  248. $instance = new $class();
  249. // Security: Check that user is not trying to call a private method
  250. if (substr(phpversion(), 0,1) == '4')
  251. {
  252. // PHP 4 check
  253. if (substr($method, 0, 1) === '_' || !method_exists($instance, $method))
  254. {
  255. // Error: The requested method either does not exist or is private (PHP 4).
  256. trigger_error("Could not find a public method in $class called $method(). (Using PHP 4 rules.)", E_USER_ERROR);
  257. }
  258. }
  259. else
  260. {
  261. // PHP 5 check
  262. $allowedMethods = get_class_methods($class);
  263. if (array_search($method, $allowedMethods) === false || substr($method, 0, 1) === '_')
  264. {
  265. // Error: The requested method either does not exist or is private (PHP 5).
  266. trigger_error("Could not find a public method in $class called $method(). (Using PHP 5 rules.)", E_USER_ERROR);
  267. }
  268. }
  269. // Strip slashes in data
  270. $dataAsPhp = stripslashes($data);
  271. // If the user did not pass an args array, treat it as
  272. // an empty args array. (Although this may be an error
  273. // on the client side, it may also be the user calling
  274. // a method that doesn't take arguments and we shouldn't
  275. // force the user to create an args parameter with an empty
  276. // array.)
  277. if ($dataAsPhp === "undefined") $dataAsPhp = "[]";
  278. // Massage special characters back (is there a better
  279. // way to do this?)
  280. $dataAsPhp = str_replace('\\t', '\t', $dataAsPhp);
  281. $dataAsPhp = str_replace('\\n', '\n', $dataAsPhp);
  282. $dataAsPhp = str_replace("\\'", "'", $dataAsPhp);
  283. // Check if there are any undefined values.
  284. if (strpos($dataAsPhp, 'undefined') !== FALSE)
  285. {
  286. // There is at least one undefined argument. This signals an error
  287. // on the Flash client (you should never pass undefined on purpose, use
  288. // null for optional arguments); signal the error to the user.
  289. $undefinedArgumentIndices = '';
  290. $numUndefinedArguments = 0;
  291. $arguments = explode(',', $dataAsPhp);
  292. $numArguments = count($arguments);
  293. for ($i = 0; $i < $numArguments; $i++)
  294. {
  295. $currentArgument = $arguments[$i];
  296. if (strpos($currentArgument, 'undefined'))
  297. {
  298. $undefinedArgumentIndices .= ', ' . $i;
  299. $numUndefinedArguments++;
  300. }
  301. }
  302. // Remove the initial comma and space.
  303. $undefinedArgumentIndices = substr($undefinedArgumentIndices, 2);
  304. // Make sure the error message is grammatically correct.
  305. $pluralization = $numUndefinedArguments > 1 ? 's' : '';
  306. $errorMsg = $numUndefinedArguments . ' undefined argument' . $pluralization . ' found at position' . $pluralization . ' ' . $undefinedArgumentIndices . ' for method '.$method.' in class '.$class.'.';
  307. trigger_error($errorMsg, E_USER_ERROR);
  308. }
  309. // Convert undefined and null to NULL
  310. //$dataAsPhp = str_replace('undefined', 'NULL', $dataAsPhp);
  311. $dataAsPhp = str_replace('null', 'NULL', $dataAsPhp);
  312. // Convert the passed JSON data to a PHP array structure.
  313. // TODO: Add error checking.
  314. // Profiling:
  315. if (LOG_ALL) $jsonStartTime = microtime_float();
  316. include_once('core/shared/util/JSON.php');
  317. $j = new Services_JSON();
  318. $dataAsPhp = $j->decode($dataAsPhp);
  319. if (LOG_ALL)
  320. {
  321. // Profiling:
  322. $jsonDuration = microtime_float() - $jsonStartTime;
  323. error_log("[SWX] PROFILING: JSON parser took $jsonDuration seconds to parse the arguments.");
  324. }
  325. // Profiling:
  326. // Service method
  327. if (LOG_ALL) $methodStartTime = microtime_float();
  328. // Call the method, passing the array's elements as individual elements.
  329. $result = call_user_func_array(array(&$instance, $method),$dataAsPhp);
  330. // Profiling:
  331. if (LOG_ALL)
  332. {
  333. $methodDuration = microtime_float() - $methodStartTime;
  334. error_log("[SWX] PROFILING: Method took $methodDuration seconds to return a result.");
  335. $duration = microtime_float() - $startTime;
  336. $swxGatewayOverhead = $duration - $jsonDuration - $methodDuration;
  337. error_log("[SWX] PROFILING: All other SWX gateway operations took $swxGatewayOverhead seconds.");
  338. // Debug:
  339. // error_log ("[SWX] INFO Method call result = $result");
  340. $swxAssemblerStartTime = microtime_float(); // Reset the timer.
  341. }
  342. // Create and write out the SWF.
  343. $swxAssembler->writeSwf($result, $debug, $compressionLevel, $url);
  344. if (LOG_ALL)
  345. {
  346. // Profiling:
  347. $swxCompilerDuration = microtime_float() - $swxAssemblerStartTime;
  348. $duration = microtime_float() - $startTime;
  349. // Status message.
  350. error_log("[SWX] PROFILING: SWF compiler took $swxCompilerDuration seconds to assemble the data SWF.");
  351. // Call profiling stats:
  352. $jsonPercentage = round($jsonDuration * 100 / $duration, 0);
  353. $swxAssemblerPercentage = round($swxCompilerDuration * 100 / $duration, 0);
  354. $methodPercentage = round($methodDuration * 100 / $duration, 0);
  355. $otherPercentage = 100 - $jsonPercentage - $swxAssemblerPercentage - $methodPercentage;
  356. error_log("[SWX] PROFILING: SWX call took $duration seconds in total, of which JSON decoding arguments: $jsonPercentage%, Service method: $methodPercentage%, SWX Data SWF assembly: $swxAssemblerPercentage%, Other: $otherPercentage%.");
  357. error_log("[SWX] PROFILING: SWX memory usage was ".memory_get_usage());
  358. // Profiler:
  359. /*
  360. error_log("[SWX] PROFILER INFO FOLLOWS:");
  361. $profileInfo = __profiler__('get');
  362. foreach ($profileInfo as $functionName => $percentage)
  363. {
  364. $inSeconds = round($duration * $percentage / 100, 3);
  365. error_log("$functionName: $percentage% ($inSeconds seconds)");
  366. }
  367. */
  368. }
  369. ?>