1<?php declare(strict_types=1);
2
3namespace ILIAS\GlobalScreen\Scope;
4
5use Closure;
6use ILIAS\UI\Component\Component;
7use LogicException;
8use ReflectionFunction;
9use ReflectionType;
10
11/**
12 * Trait ComponentDecoratorTrait
13 *
14 * @package ILIAS\GlobalScreen\Scope
15 *
16 * @author  Fabian Schmid <fs@studer-raimann.ch>
17 */
18trait ComponentDecoratorTrait
19{
20
21    /**
22     * @var Closure
23     */
24    private $component_decorator;
25
26
27    /**
28     * @param Closure $component_decorator
29     *
30     * @return isGlobalScreenItem
31     */
32    public function addComponentDecorator(Closure $component_decorator) : isGlobalScreenItem
33    {
34        if (!$this->checkClosure($component_decorator)) {
35            throw new LogicException('first argument and return value of closure must be type-hinted to \ILIAS\UI\Component\Component');
36        }
37        if ($this->component_decorator instanceof Closure) {
38            $existing = $this->component_decorator;
39            $this->component_decorator = static function (Component $c) use ($component_decorator, $existing) : Component {
40                $component = $existing();
41
42                return $component_decorator($component);
43            };
44        } else {
45            $this->component_decorator = $component_decorator;
46        }
47
48        return $this;
49    }
50
51
52    /**
53     * @return Closure|null
54     */
55    public function getComponentDecorator() : ?Closure
56    {
57        return $this->component_decorator;
58    }
59
60
61    private function checkClosure(Closure $c) : bool
62    {
63        try {
64            $r = new ReflectionFunction($c);
65            if (count($r->getParameters()) !== 1) {
66                return false;
67            }
68            $first_param_type = $r->getParameters()[0]->getType();
69            if ($first_param_type instanceof ReflectionType && $first_param_type->getName() !== Component::class) {
70                return false;
71            }
72            $return_type = $r->getReturnType();
73            if ($return_type === null) {
74                return false;
75            }
76            if ($return_type->getName() !== Component::class) {
77                return false;
78            }
79
80            return true;
81        } catch (\Throwable $i) {
82            return false;
83        }
84    }
85}
86