You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

180 lines
4.2 KiB

  1. <?php
  2. namespace dokuwiki\Remote;
  3. use dokuwiki\Remote\OpenApiDoc\DocBlockMethod;
  4. use InvalidArgumentException;
  5. use ReflectionException;
  6. use ReflectionFunction;
  7. use ReflectionMethod;
  8. use RuntimeException;
  9. class ApiCall
  10. {
  11. /** @var callable The method to be called for this endpoint */
  12. protected $method;
  13. /** @var bool Whether this call can be called without authentication */
  14. protected bool $isPublic = false;
  15. /** @var string The category this call belongs to */
  16. protected string $category;
  17. /** @var DocBlockMethod The meta data of this call as parsed from its doc block */
  18. protected $docs;
  19. /**
  20. * Make the given method available as an API call
  21. *
  22. * @param string|array $method Either [object,'method'] or 'function'
  23. * @param string $category The category this call belongs to
  24. */
  25. public function __construct($method, $category = '')
  26. {
  27. if (!is_callable($method)) {
  28. throw new InvalidArgumentException('Method is not callable');
  29. }
  30. $this->method = $method;
  31. $this->category = $category;
  32. }
  33. /**
  34. * Call the method
  35. *
  36. * Important: access/authentication checks need to be done before calling this!
  37. *
  38. * @param array $args
  39. * @return mixed
  40. */
  41. public function __invoke($args)
  42. {
  43. if (!array_is_list($args)) {
  44. $args = $this->namedArgsToPositional($args);
  45. }
  46. return call_user_func_array($this->method, $args);
  47. }
  48. /**
  49. * Access the method documentation
  50. *
  51. * This lazy loads the docs only when needed
  52. *
  53. * @return DocBlockMethod
  54. */
  55. public function getDocs()
  56. {
  57. if ($this->docs === null) {
  58. try {
  59. if (is_array($this->method)) {
  60. $reflect = new ReflectionMethod($this->method[0], $this->method[1]);
  61. } else {
  62. $reflect = new ReflectionFunction($this->method);
  63. }
  64. $this->docs = new DocBlockMethod($reflect);
  65. } catch (ReflectionException $e) {
  66. throw new RuntimeException('Failed to parse API method documentation', 0, $e);
  67. }
  68. }
  69. return $this->docs;
  70. }
  71. /**
  72. * Is this a public method?
  73. *
  74. * Public methods can be called without authentication
  75. *
  76. * @return bool
  77. */
  78. public function isPublic()
  79. {
  80. return $this->isPublic;
  81. }
  82. /**
  83. * Set the public flag
  84. *
  85. * @param bool $isPublic
  86. * @return $this
  87. */
  88. public function setPublic(bool $isPublic = true)
  89. {
  90. $this->isPublic = $isPublic;
  91. return $this;
  92. }
  93. /**
  94. * Get information about the argument of this call
  95. *
  96. * @return array
  97. */
  98. public function getArgs()
  99. {
  100. return $this->getDocs()->getParameters();
  101. }
  102. /**
  103. * Get information about the return value of this call
  104. *
  105. * @return array
  106. */
  107. public function getReturn()
  108. {
  109. return $this->getDocs()->getReturn();
  110. }
  111. /**
  112. * Get the summary of this call
  113. *
  114. * @return string
  115. */
  116. public function getSummary()
  117. {
  118. return $this->getDocs()->getSummary();
  119. }
  120. /**
  121. * Get the description of this call
  122. *
  123. * @return string
  124. */
  125. public function getDescription()
  126. {
  127. return $this->getDocs()->getDescription();
  128. }
  129. /**
  130. * Get the category of this call
  131. *
  132. * @return string
  133. */
  134. public function getCategory()
  135. {
  136. return $this->category;
  137. }
  138. /**
  139. * Converts named arguments to positional arguments
  140. *
  141. * @fixme with PHP 8 we can use named arguments directly using the spread operator
  142. * @param array $params
  143. * @return array
  144. */
  145. protected function namedArgsToPositional($params)
  146. {
  147. $args = [];
  148. foreach ($this->getDocs()->getParameters() as $arg => $arginfo) {
  149. if (isset($params[$arg])) {
  150. $args[] = $params[$arg];
  151. } elseif ($arginfo['optional'] && array_key_exists('default', $arginfo)) {
  152. $args[] = $arginfo['default'];
  153. } else {
  154. throw new InvalidArgumentException("Missing argument $arg");
  155. }
  156. }
  157. return $args;
  158. }
  159. }