ServiceRouter.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <?php
  2. /**
  3. * This file is part of amfPHP
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the license that is bundled
  8. * with this package in the file license.txt.
  9. */
  10. /**
  11. * The Service Router class is responsible for executing the remote service method and returning it's value.
  12. * based on the old 'Executive' of php 1.9. It looks for a service either explicitely defined in a
  13. * ClassFindInfo object, or in a service folder.
  14. *
  15. * @package Amfphp_Core_Common
  16. * @author Ariel Sommeria-klein
  17. */
  18. class Amfphp_Core_Common_ServiceRouter {
  19. /**
  20. * filter called when the service object is created. Useful for authentication
  21. * @param Object $serviceObject
  22. * @param string $serviceName
  23. * @param string $methodName
  24. * @param array $parameters
  25. */
  26. const FILTER_SERVICE_OBJECT = 'FILTER_SERVICE_OBJECT';
  27. /**
  28. * paths to folders containing services(relative or absolute)
  29. * @var array of paths
  30. */
  31. public $serviceFolders;
  32. /**
  33. *
  34. * @var array of ClassFindInfo
  35. */
  36. public $serviceNames2ClassFindInfo;
  37. /**
  38. * check parameters. This is useful for development, but should be disabled for production
  39. * @var Boolean
  40. */
  41. public $checkArgumentCount;
  42. /**
  43. * constructor
  44. * @param array $serviceFolders folders containing service classes
  45. * @param array $serviceNames2ClassFindInfo a dictionary of service classes represented in a ClassFindInfo.
  46. * @param Boolean $checkArgumentCount
  47. */
  48. public function __construct($serviceFolders, $serviceNames2ClassFindInfo, $checkArgumentCount = false) {
  49. $this->serviceFolders = $serviceFolders;
  50. $this->serviceNames2ClassFindInfo = $serviceNames2ClassFindInfo;
  51. $this->checkArgumentCount = $checkArgumentCount;
  52. }
  53. /**
  54. * get a service object by its name. Looks for a match in serviceNames2ClassFindInfo, then in the defined service folders.
  55. * If none found, an exception is thrown
  56. * this method is static so that it can be used also by the discovery service
  57. * '__' are replaced by '/' to help the client generator support packages without messing with folders and the like
  58. * the service object can either be in the global namespace or in the namespace suggested by the name.
  59. * For example a call to Sub1/Sub2/NamespaceTestService will load the PHP file in Sub1/Sub2/NamespaceTestService,
  60. * and return an instance of either NamespaceTestService or Sub1\Sub2\NamespaceTestService
  61. *
  62. * @param type $serviceName
  63. * @param array $serviceFolders
  64. * @param array $serviceNames2ClassFindInfo
  65. * @return Object service object
  66. */
  67. public static function getServiceObjectStatically($serviceName, array $serviceFolders, array $serviceNames2ClassFindInfo){
  68. $serviceObject = null;
  69. if (isset($serviceNames2ClassFindInfo[$serviceName])) {
  70. $classFindInfo = $serviceNames2ClassFindInfo[$serviceName];
  71. require_once $classFindInfo->absolutePath;
  72. $serviceObject = new $classFindInfo->className();
  73. } else {
  74. $temp = str_replace('.', '/', $serviceName);
  75. $serviceNameWithSlashes = str_replace('__', '/', $temp);
  76. $serviceIncludePath = $serviceNameWithSlashes . '.php';
  77. $exploded = explode('/', $serviceNameWithSlashes);
  78. $className = $exploded[count($exploded) - 1];
  79. //no class find info. try to look in the folders
  80. foreach ($serviceFolders as $folder) {
  81. $folderPath = NULL;
  82. $rootNamespace = NULL;
  83. if(is_array($folder)){
  84. $rootNamespace = $folder[1];
  85. $folderPath = $folder[0];
  86. }else{
  87. $folderPath = $folder;
  88. }
  89. $servicePath = $folderPath . $serviceIncludePath;
  90. if (file_exists($servicePath)) {
  91. require_once $servicePath;
  92. if($rootNamespace == NULL){
  93. $serviceObject = new $className();
  94. }else{
  95. $namespacedClassName = $rootNamespace . '\\' . str_replace('/', '\\', $serviceNameWithSlashes);
  96. $serviceObject = new $namespacedClassName;
  97. }
  98. }
  99. }
  100. }
  101. if (!$serviceObject) {
  102. throw new Amfphp_Core_Exception("$serviceName service not found ");
  103. }
  104. return $serviceObject;
  105. }
  106. /**
  107. * get service object
  108. * @param String $serviceName
  109. * @return Object service object
  110. */
  111. public function getServiceObject($serviceName) {
  112. return self::getServiceObjectStatically($serviceName, $this->serviceFolders, $this->serviceNames2ClassFindInfo);
  113. }
  114. /**
  115. * loads and instanciates a service class matching $serviceName, then calls the function defined by $methodName using $parameters as parameters
  116. * throws an exception if service not found.
  117. * if the service exists but not the function, an exception is thrown by call_user_func_array. It is pretty explicit, so no further code was added
  118. *
  119. * @param string $serviceName
  120. * @param string $methodName
  121. * @param array $parameters
  122. * @return mixed the result of the function call
  123. *
  124. */
  125. public function executeServiceCall($serviceName, $methodName, array $parameters) {
  126. $unfilteredServiceObject = $this->getServiceObject($serviceName);
  127. $serviceObject = Amfphp_Core_FilterManager::getInstance()->callFilters(self::FILTER_SERVICE_OBJECT, $unfilteredServiceObject, $serviceName, $methodName, $parameters);
  128. $isStaticMethod = false;
  129. if(method_exists($serviceObject, $methodName)){
  130. //method exists, but isn't static
  131. }else if (method_exists($serviceName, $methodName)) {
  132. $isStaticMethod = true;
  133. }else{
  134. throw new Amfphp_Core_Exception("method $methodName not found on $serviceName object ");
  135. }
  136. if(substr($methodName, 0, 1) == '_'){
  137. throw new Exception("The method $methodName starts with a '_', and is therefore not accessible");
  138. }
  139. if($this->checkArgumentCount){
  140. $method = new ReflectionMethod($serviceObject, $methodName);
  141. $numberOfRequiredParameters = $method->getNumberOfRequiredParameters();
  142. $numberOfParameters = $method->getNumberOfParameters();
  143. $numberOfProvidedParameters = count($parameters);
  144. if ($numberOfProvidedParameters < $numberOfRequiredParameters || $numberOfProvidedParameters > $numberOfParameters) {
  145. throw new Amfphp_Core_Exception("Invalid number of parameters for method $methodName in service $serviceName : $numberOfRequiredParameters required, $numberOfParameters total, $numberOfProvidedParameters provided");
  146. }
  147. }
  148. if($isStaticMethod){
  149. return call_user_func_array(array($serviceName, $methodName), $parameters);
  150. }else{
  151. return call_user_func_array(array($serviceObject, $methodName), $parameters);
  152. }
  153. }
  154. }
  155. ?>