1<?php 2 3namespace dokuwiki\Remote; 4 5 6use dokuwiki\Remote\OpenApiDoc\DocBlockMethod; 7 8class ApiCall 9{ 10 /** @var callable The method to be called for this endpoint */ 11 protected $method; 12 13 /** @var bool Whether this call can be called without authentication */ 14 protected bool $isPublic = false; 15 16 /** @var DocBlockMethod The meta data of this call as parsed from its doc block */ 17 protected $docs; 18 19 /** 20 * Make the given method available as an API call 21 * 22 * @param string|array $method Either [object,'method'] or 'function' 23 */ 24 public function __construct($method) 25 { 26 if (!is_callable($method)) { 27 throw new \InvalidArgumentException('Method is not callable'); 28 } 29 30 $this->method = $method; 31 } 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 /** 50 * Access the method documentation 51 * 52 * This lazy loads the docs only when needed 53 * 54 * @return DocBlockMethod 55 */ 56 public function getDocs() 57 { 58 if ($this->docs === null) { 59 try { 60 if (is_array($this->method)) { 61 $reflect = new \ReflectionMethod($this->method[0], $this->method[1]); 62 } else { 63 $reflect = new \ReflectionFunction($this->method); 64 } 65 $this->docs = new DocBlockMethod($reflect); 66 } catch (\ReflectionException $e) { 67 throw new \RuntimeException('Failed to parse API method documentation', 0, $e); 68 } 69 70 } 71 return $this->docs; 72 } 73 74 75 /** 76 * @return bool 77 */ 78 public function isPublic(): bool 79 { 80 return $this->isPublic; 81 } 82 83 /** 84 * @param bool $isPublic 85 * @return $this 86 */ 87 public function setPublic(bool $isPublic = true): self 88 { 89 $this->isPublic = $isPublic; 90 return $this; 91 } 92 93 94 /** 95 * @return array 96 */ 97 public function getArgs(): array 98 { 99 return $this->getDocs()->getParameters(); 100 } 101 102 /** 103 * @return array 104 */ 105 public function getReturn(): array 106 { 107 return $this->getDocs()->getReturn(); 108 } 109 110 /** 111 * @return string 112 */ 113 public function getSummary(): string 114 { 115 return $this->getDocs()->getSummary(); 116 } 117 118 /** 119 * @return string 120 */ 121 public function getDescription(): string 122 { 123 return $this->getDocs()->getDescription(); 124 } 125 126 /** 127 * Converts named arguments to positional arguments 128 * 129 * @fixme with PHP 8 we can use named arguments directly using the spread operator 130 * @param array $params 131 * @return array 132 */ 133 protected function namedArgsToPositional($params) 134 { 135 $args = []; 136 137 foreach (array_keys($this->docs->getParameters()) as $arg) { 138 if (isset($params[$arg])) { 139 $args[] = $params[$arg]; 140 } else { 141 $args[] = null; 142 } 143 } 144 145 return $args; 146 } 147 148} 149