xref: /dokuwiki/inc/Remote/ApiCall.php (revision d48c2b252a339bed693da46fae768d93f4b3fe41)
142e66c7aSAndreas Gohr<?php
242e66c7aSAndreas Gohr
342e66c7aSAndreas Gohrnamespace dokuwiki\Remote;
442e66c7aSAndreas Gohr
5dd7472d3SAndreas Gohruse dokuwiki\Remote\OpenApiDoc\DocBlockMethod;
66cce3332SAndreas Gohruse InvalidArgumentException;
76cce3332SAndreas Gohruse ReflectionException;
86cce3332SAndreas Gohruse ReflectionFunction;
96cce3332SAndreas Gohruse ReflectionMethod;
106cce3332SAndreas Gohruse RuntimeException;
11dd7472d3SAndreas Gohr
1242e66c7aSAndreas Gohrclass ApiCall
1342e66c7aSAndreas Gohr{
1442e66c7aSAndreas Gohr    /** @var callable The method to be called for this endpoint */
1542e66c7aSAndreas Gohr    protected $method;
1642e66c7aSAndreas Gohr
1742e66c7aSAndreas Gohr    /** @var bool Whether this call can be called without authentication */
1842e66c7aSAndreas Gohr    protected bool $isPublic = false;
1942e66c7aSAndreas Gohr
206cce3332SAndreas Gohr    /** @var string The category this call belongs to */
216cce3332SAndreas Gohr    protected string $category;
226cce3332SAndreas Gohr
23dd7472d3SAndreas Gohr    /** @var DocBlockMethod The meta data of this call as parsed from its doc block */
24dd7472d3SAndreas Gohr    protected $docs;
2566f07661SAndreas Gohr
2642e66c7aSAndreas Gohr    /**
2742e66c7aSAndreas Gohr     * Make the given method available as an API call
2842e66c7aSAndreas Gohr     *
2942e66c7aSAndreas Gohr     * @param string|array $method Either [object,'method'] or 'function'
306cce3332SAndreas Gohr     * @param string $category The category this call belongs to
3142e66c7aSAndreas Gohr     */
326cce3332SAndreas Gohr    public function __construct($method, $category = '')
3342e66c7aSAndreas Gohr    {
3442e66c7aSAndreas Gohr        if (!is_callable($method)) {
356cce3332SAndreas Gohr            throw new InvalidArgumentException('Method is not callable');
3642e66c7aSAndreas Gohr        }
3742e66c7aSAndreas Gohr
3842e66c7aSAndreas Gohr        $this->method = $method;
396cce3332SAndreas Gohr        $this->category = $category;
4042e66c7aSAndreas Gohr    }
4142e66c7aSAndreas Gohr
4242e66c7aSAndreas Gohr    /**
4342e66c7aSAndreas Gohr     * Call the method
4442e66c7aSAndreas Gohr     *
4542e66c7aSAndreas Gohr     * Important: access/authentication checks need to be done before calling this!
4642e66c7aSAndreas Gohr     *
4742e66c7aSAndreas Gohr     * @param array $args
4842e66c7aSAndreas Gohr     * @return mixed
4942e66c7aSAndreas Gohr     */
5042e66c7aSAndreas Gohr    public function __invoke($args)
5142e66c7aSAndreas Gohr    {
5242e66c7aSAndreas Gohr        if (!array_is_list($args)) {
5342e66c7aSAndreas Gohr            $args = $this->namedArgsToPositional($args);
5442e66c7aSAndreas Gohr        }
5542e66c7aSAndreas Gohr        return call_user_func_array($this->method, $args);
5642e66c7aSAndreas Gohr    }
5742e66c7aSAndreas Gohr
5842e66c7aSAndreas Gohr    /**
59dd7472d3SAndreas Gohr     * Access the method documentation
60dd7472d3SAndreas Gohr     *
61dd7472d3SAndreas Gohr     * This lazy loads the docs only when needed
62dd7472d3SAndreas Gohr     *
63dd7472d3SAndreas Gohr     * @return DocBlockMethod
64dd7472d3SAndreas Gohr     */
65dd7472d3SAndreas Gohr    public function getDocs()
66dd7472d3SAndreas Gohr    {
67dd7472d3SAndreas Gohr        if ($this->docs === null) {
68dd7472d3SAndreas Gohr            try {
69dd7472d3SAndreas Gohr                if (is_array($this->method)) {
706cce3332SAndreas Gohr                    $reflect = new ReflectionMethod($this->method[0], $this->method[1]);
71dd7472d3SAndreas Gohr                } else {
726cce3332SAndreas Gohr                    $reflect = new ReflectionFunction($this->method);
73dd7472d3SAndreas Gohr                }
74dd7472d3SAndreas Gohr                $this->docs = new DocBlockMethod($reflect);
756cce3332SAndreas Gohr            } catch (ReflectionException $e) {
766cce3332SAndreas Gohr                throw new RuntimeException('Failed to parse API method documentation', 0, $e);
77dd7472d3SAndreas Gohr            }
78dd7472d3SAndreas Gohr        }
79dd7472d3SAndreas Gohr        return $this->docs;
80dd7472d3SAndreas Gohr    }
81dd7472d3SAndreas Gohr
82dd7472d3SAndreas Gohr    /**
83*d48c2b25SAndreas Gohr     * Is this a public method?
84*d48c2b25SAndreas Gohr     *
85*d48c2b25SAndreas Gohr     * Public methods can be called without authentication
86*d48c2b25SAndreas Gohr     *
8742e66c7aSAndreas Gohr     * @return bool
8842e66c7aSAndreas Gohr     */
89*d48c2b25SAndreas Gohr    public function isPublic()
9042e66c7aSAndreas Gohr    {
9142e66c7aSAndreas Gohr        return $this->isPublic;
9242e66c7aSAndreas Gohr    }
9342e66c7aSAndreas Gohr
9442e66c7aSAndreas Gohr    /**
95*d48c2b25SAndreas Gohr     * Set the public flag
96*d48c2b25SAndreas Gohr     *
9742e66c7aSAndreas Gohr     * @param bool $isPublic
9842e66c7aSAndreas Gohr     * @return $this
9942e66c7aSAndreas Gohr     */
100*d48c2b25SAndreas Gohr    public function setPublic(bool $isPublic = true)
10142e66c7aSAndreas Gohr    {
10242e66c7aSAndreas Gohr        $this->isPublic = $isPublic;
10342e66c7aSAndreas Gohr        return $this;
10442e66c7aSAndreas Gohr    }
10542e66c7aSAndreas Gohr
10642e66c7aSAndreas Gohr    /**
107*d48c2b25SAndreas Gohr     * Get information about the argument of this call
108*d48c2b25SAndreas Gohr     *
10942e66c7aSAndreas Gohr     * @return array
11042e66c7aSAndreas Gohr     */
111*d48c2b25SAndreas Gohr    public function getArgs()
11242e66c7aSAndreas Gohr    {
113dd7472d3SAndreas Gohr        return $this->getDocs()->getParameters();
11442e66c7aSAndreas Gohr    }
11542e66c7aSAndreas Gohr
11642e66c7aSAndreas Gohr    /**
117*d48c2b25SAndreas Gohr     * Get information about the return value of this call
118*d48c2b25SAndreas Gohr     *
11942e66c7aSAndreas Gohr     * @return array
12042e66c7aSAndreas Gohr     */
121*d48c2b25SAndreas Gohr    public function getReturn()
12242e66c7aSAndreas Gohr    {
123dd7472d3SAndreas Gohr        return $this->getDocs()->getReturn();
12442e66c7aSAndreas Gohr    }
12542e66c7aSAndreas Gohr
12642e66c7aSAndreas Gohr    /**
127*d48c2b25SAndreas Gohr     * Get the summary of this call
128*d48c2b25SAndreas Gohr     *
12942e66c7aSAndreas Gohr     * @return string
13042e66c7aSAndreas Gohr     */
131*d48c2b25SAndreas Gohr    public function getSummary()
13242e66c7aSAndreas Gohr    {
133dd7472d3SAndreas Gohr        return $this->getDocs()->getSummary();
13442e66c7aSAndreas Gohr    }
13542e66c7aSAndreas Gohr
13642e66c7aSAndreas Gohr    /**
137*d48c2b25SAndreas Gohr     * Get the description of this call
138*d48c2b25SAndreas Gohr     *
13942e66c7aSAndreas Gohr     * @return string
14042e66c7aSAndreas Gohr     */
141*d48c2b25SAndreas Gohr    public function getDescription()
14242e66c7aSAndreas Gohr    {
143dd7472d3SAndreas Gohr        return $this->getDocs()->getDescription();
14442e66c7aSAndreas Gohr    }
14542e66c7aSAndreas Gohr
14642e66c7aSAndreas Gohr    /**
147*d48c2b25SAndreas Gohr     * Get the category of this call
148*d48c2b25SAndreas Gohr     *
1496cce3332SAndreas Gohr     * @return string
1506cce3332SAndreas Gohr     */
151*d48c2b25SAndreas Gohr    public function getCategory()
1526cce3332SAndreas Gohr    {
1536cce3332SAndreas Gohr        return $this->category;
1546cce3332SAndreas Gohr    }
1556cce3332SAndreas Gohr
1566cce3332SAndreas Gohr    /**
15742e66c7aSAndreas Gohr     * Converts named arguments to positional arguments
15842e66c7aSAndreas Gohr     *
15942e66c7aSAndreas Gohr     * @fixme with PHP 8 we can use named arguments directly using the spread operator
16042e66c7aSAndreas Gohr     * @param array $params
16142e66c7aSAndreas Gohr     * @return array
16242e66c7aSAndreas Gohr     */
16342e66c7aSAndreas Gohr    protected function namedArgsToPositional($params)
16442e66c7aSAndreas Gohr    {
16542e66c7aSAndreas Gohr        $args = [];
16642e66c7aSAndreas Gohr
1677de5ac55SAndreas Gohr        foreach ($this->getDocs()->getParameters() as $arg => $arginfo) {
16842e66c7aSAndreas Gohr            if (isset($params[$arg])) {
16942e66c7aSAndreas Gohr                $args[] = $params[$arg];
170*d48c2b25SAndreas Gohr            } elseif ($arginfo['optional'] && array_key_exists('default', $arginfo)) {
1717de5ac55SAndreas Gohr                $args[] = $arginfo['default'];
1727de5ac55SAndreas Gohr            } else {
1737de5ac55SAndreas Gohr                throw new InvalidArgumentException("Missing argument $arg");
1747de5ac55SAndreas Gohr            }
17542e66c7aSAndreas Gohr        }
17642e66c7aSAndreas Gohr
17742e66c7aSAndreas Gohr        return $args;
17842e66c7aSAndreas Gohr    }
17942e66c7aSAndreas Gohr}
180