142e66c7aSAndreas Gohr<?php 242e66c7aSAndreas Gohr 342e66c7aSAndreas Gohrnamespace dokuwiki\Remote; 442e66c7aSAndreas Gohr 542e66c7aSAndreas Gohr 6dd7472d3SAndreas Gohruse dokuwiki\Remote\OpenApiDoc\DocBlockMethod; 7*6cce3332SAndreas Gohruse InvalidArgumentException; 8*6cce3332SAndreas Gohruse ReflectionException; 9*6cce3332SAndreas Gohruse ReflectionFunction; 10*6cce3332SAndreas Gohruse ReflectionMethod; 11*6cce3332SAndreas Gohruse RuntimeException; 12dd7472d3SAndreas Gohr 1342e66c7aSAndreas Gohrclass ApiCall 1442e66c7aSAndreas Gohr{ 1542e66c7aSAndreas Gohr /** @var callable The method to be called for this endpoint */ 1642e66c7aSAndreas Gohr protected $method; 1742e66c7aSAndreas Gohr 1842e66c7aSAndreas Gohr /** @var bool Whether this call can be called without authentication */ 1942e66c7aSAndreas Gohr protected bool $isPublic = false; 2042e66c7aSAndreas Gohr 21*6cce3332SAndreas Gohr /** @var string The category this call belongs to */ 22*6cce3332SAndreas Gohr protected string $category; 23*6cce3332SAndreas Gohr 24dd7472d3SAndreas Gohr /** @var DocBlockMethod The meta data of this call as parsed from its doc block */ 25dd7472d3SAndreas Gohr protected $docs; 2666f07661SAndreas Gohr 2742e66c7aSAndreas Gohr /** 2842e66c7aSAndreas Gohr * Make the given method available as an API call 2942e66c7aSAndreas Gohr * 3042e66c7aSAndreas Gohr * @param string|array $method Either [object,'method'] or 'function' 31*6cce3332SAndreas Gohr * @param string $category The category this call belongs to 3242e66c7aSAndreas Gohr */ 33*6cce3332SAndreas Gohr public function __construct($method, $category = '') 3442e66c7aSAndreas Gohr { 3542e66c7aSAndreas Gohr if (!is_callable($method)) { 36*6cce3332SAndreas Gohr throw new InvalidArgumentException('Method is not callable'); 3742e66c7aSAndreas Gohr } 3842e66c7aSAndreas Gohr 3942e66c7aSAndreas Gohr $this->method = $method; 40*6cce3332SAndreas Gohr $this->category = $category; 4142e66c7aSAndreas Gohr } 4242e66c7aSAndreas Gohr 4342e66c7aSAndreas Gohr /** 4442e66c7aSAndreas Gohr * Call the method 4542e66c7aSAndreas Gohr * 4642e66c7aSAndreas Gohr * Important: access/authentication checks need to be done before calling this! 4742e66c7aSAndreas Gohr * 4842e66c7aSAndreas Gohr * @param array $args 4942e66c7aSAndreas Gohr * @return mixed 5042e66c7aSAndreas Gohr */ 5142e66c7aSAndreas Gohr public function __invoke($args) 5242e66c7aSAndreas Gohr { 5342e66c7aSAndreas Gohr if (!array_is_list($args)) { 5442e66c7aSAndreas Gohr $args = $this->namedArgsToPositional($args); 5542e66c7aSAndreas Gohr } 5642e66c7aSAndreas Gohr return call_user_func_array($this->method, $args); 5742e66c7aSAndreas Gohr } 5842e66c7aSAndreas Gohr 5942e66c7aSAndreas Gohr /** 60dd7472d3SAndreas Gohr * Access the method documentation 61dd7472d3SAndreas Gohr * 62dd7472d3SAndreas Gohr * This lazy loads the docs only when needed 63dd7472d3SAndreas Gohr * 64dd7472d3SAndreas Gohr * @return DocBlockMethod 65dd7472d3SAndreas Gohr */ 66dd7472d3SAndreas Gohr public function getDocs() 67dd7472d3SAndreas Gohr { 68dd7472d3SAndreas Gohr if ($this->docs === null) { 69dd7472d3SAndreas Gohr try { 70dd7472d3SAndreas Gohr if (is_array($this->method)) { 71*6cce3332SAndreas Gohr $reflect = new ReflectionMethod($this->method[0], $this->method[1]); 72dd7472d3SAndreas Gohr } else { 73*6cce3332SAndreas Gohr $reflect = new ReflectionFunction($this->method); 74dd7472d3SAndreas Gohr } 75dd7472d3SAndreas Gohr $this->docs = new DocBlockMethod($reflect); 76*6cce3332SAndreas Gohr } catch (ReflectionException $e) { 77*6cce3332SAndreas Gohr throw new RuntimeException('Failed to parse API method documentation', 0, $e); 78dd7472d3SAndreas Gohr } 79dd7472d3SAndreas Gohr 80dd7472d3SAndreas Gohr } 81dd7472d3SAndreas Gohr return $this->docs; 82dd7472d3SAndreas Gohr } 83dd7472d3SAndreas Gohr 84dd7472d3SAndreas Gohr 85dd7472d3SAndreas Gohr /** 8642e66c7aSAndreas Gohr * @return bool 8742e66c7aSAndreas Gohr */ 8842e66c7aSAndreas Gohr public function isPublic(): bool 8942e66c7aSAndreas Gohr { 9042e66c7aSAndreas Gohr return $this->isPublic; 9142e66c7aSAndreas Gohr } 9242e66c7aSAndreas Gohr 9342e66c7aSAndreas Gohr /** 9442e66c7aSAndreas Gohr * @param bool $isPublic 9542e66c7aSAndreas Gohr * @return $this 9642e66c7aSAndreas Gohr */ 9742e66c7aSAndreas Gohr public function setPublic(bool $isPublic = true): self 9842e66c7aSAndreas Gohr { 9942e66c7aSAndreas Gohr $this->isPublic = $isPublic; 10042e66c7aSAndreas Gohr return $this; 10142e66c7aSAndreas Gohr } 10242e66c7aSAndreas Gohr 10342e66c7aSAndreas Gohr 10442e66c7aSAndreas Gohr /** 10542e66c7aSAndreas Gohr * @return array 10642e66c7aSAndreas Gohr */ 10742e66c7aSAndreas Gohr public function getArgs(): array 10842e66c7aSAndreas Gohr { 109dd7472d3SAndreas Gohr return $this->getDocs()->getParameters(); 11042e66c7aSAndreas Gohr } 11142e66c7aSAndreas Gohr 11242e66c7aSAndreas Gohr /** 11342e66c7aSAndreas Gohr * @return array 11442e66c7aSAndreas Gohr */ 11542e66c7aSAndreas Gohr public function getReturn(): array 11642e66c7aSAndreas Gohr { 117dd7472d3SAndreas Gohr return $this->getDocs()->getReturn(); 11842e66c7aSAndreas Gohr } 11942e66c7aSAndreas Gohr 12042e66c7aSAndreas Gohr /** 12142e66c7aSAndreas Gohr * @return string 12242e66c7aSAndreas Gohr */ 12342e66c7aSAndreas Gohr public function getSummary(): string 12442e66c7aSAndreas Gohr { 125dd7472d3SAndreas Gohr return $this->getDocs()->getSummary(); 12642e66c7aSAndreas Gohr } 12742e66c7aSAndreas Gohr 12842e66c7aSAndreas Gohr /** 12942e66c7aSAndreas Gohr * @return string 13042e66c7aSAndreas Gohr */ 13142e66c7aSAndreas Gohr public function getDescription(): string 13242e66c7aSAndreas Gohr { 133dd7472d3SAndreas Gohr return $this->getDocs()->getDescription(); 13442e66c7aSAndreas Gohr } 13542e66c7aSAndreas Gohr 13642e66c7aSAndreas Gohr /** 137*6cce3332SAndreas Gohr * @return string 138*6cce3332SAndreas Gohr */ 139*6cce3332SAndreas Gohr public function getCategory(): string 140*6cce3332SAndreas Gohr { 141*6cce3332SAndreas Gohr return $this->category; 142*6cce3332SAndreas Gohr } 143*6cce3332SAndreas Gohr 144*6cce3332SAndreas Gohr /** 14542e66c7aSAndreas Gohr * Converts named arguments to positional arguments 14642e66c7aSAndreas Gohr * 14742e66c7aSAndreas Gohr * @fixme with PHP 8 we can use named arguments directly using the spread operator 14842e66c7aSAndreas Gohr * @param array $params 14942e66c7aSAndreas Gohr * @return array 15042e66c7aSAndreas Gohr */ 15142e66c7aSAndreas Gohr protected function namedArgsToPositional($params) 15242e66c7aSAndreas Gohr { 15342e66c7aSAndreas Gohr $args = []; 15442e66c7aSAndreas Gohr 155*6cce3332SAndreas Gohr foreach (array_keys($this->getDocs()->getParameters()) as $arg) { 15642e66c7aSAndreas Gohr if (isset($params[$arg])) { 15742e66c7aSAndreas Gohr $args[] = $params[$arg]; 15842e66c7aSAndreas Gohr } else { 15942e66c7aSAndreas Gohr $args[] = null; 16042e66c7aSAndreas Gohr } 16142e66c7aSAndreas Gohr } 16242e66c7aSAndreas Gohr 16342e66c7aSAndreas Gohr return $args; 16442e66c7aSAndreas Gohr } 16542e66c7aSAndreas Gohr 16642e66c7aSAndreas Gohr} 167