xref: /dokuwiki/inc/Remote/ApiCall.php (revision dd7472d3f89d3f537224163206d7dbf31ffdad22)
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