Completed
Push — master ( 7b9575...49c7e6 )
by Maksim
17s
created

Indexable::buildCallback()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 9
cts 9
cp 1
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 9
nc 4
nop 2
crap 5
1
<?php
2
3
/*
4
 * This file is part of the FOSElasticaBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
/**
13
 * This file is part of the FOSElasticaBundle project.
14
 *
15
 * (c) FriendsOfSymfony <https://github.com/FriendsOfSymfony/FOSElasticaBundle/graphs/contributors>
16
 *
17
 * For the full copyright and license information, please view the LICENSE
18
 * file that was distributed with this source code.
19
 */
20
21
namespace FOS\ElasticaBundle\Provider;
22
23
use Symfony\Component\ExpressionLanguage\Expression;
24
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
25
use Symfony\Component\ExpressionLanguage\SyntaxError;
26
27
class Indexable implements IndexableInterface
28
{
29
    /**
30
     * An array of raw configured callbacks for all types.
31
     *
32
     * @var array
33
     */
34
    private $callbacks = [];
35
36
    /**
37
     * An instance of ExpressionLanguage.
38
     *
39
     * @var ExpressionLanguage
40
     */
41
    private $expressionLanguage;
42
43
    /**
44
     * An array of initialised callbacks.
45
     *
46
     * @var array
47
     */
48
    private $initialisedCallbacks = [];
49
50 19
    public function __construct(array $callbacks)
51
    {
52 19
        $this->callbacks = $callbacks;
53 19
    }
54
55
    /**
56
     * Return whether the object is indexable with respect to the callback.
57
     *
58
     * @param string $indexName
59
     * @param string $typeName
60
     * @param mixed  $object
61
     *
62
     * @return bool
63
     */
64 15
    public function isObjectIndexable($indexName, $typeName, $object)
65
    {
66 15
        $type = sprintf('%s/%s', $indexName, $typeName);
67 15
        $callback = $this->getCallback($type, $object);
68 11
        if (!$callback) {
69 1
            return true;
70
        }
71
72 10
        if ($callback instanceof Expression) {
73 6
            return (bool) $this->getExpressionLanguage()->evaluate($callback, [
74 6
                'object' => $object,
75 6
                $this->getExpressionVar($object) => $object,
76
            ]);
77
        }
78
79 5
        return is_string($callback)
80 2
            ? call_user_func([$object, $callback])
81 5
            : call_user_func($callback, $object);
82
    }
83
84
    /**
85
     * Builds and initialises a callback.
86
     *
87
     * @param string $type
88
     * @param object $object
89
     *
90
     * @return callable|string|ExpressionLanguage|null
91
     */
92 15
    private function buildCallback($type, $object)
93
    {
94 15
        if (!array_key_exists($type, $this->callbacks)) {
95 1
            return null;
96
        }
97
98 14
        $callback = $this->callbacks[$type];
99
100 14
        if (is_callable($callback) or is_callable([$object, $callback])) {
101 5
            return $callback;
102
        }
103
104 10
        if (is_string($callback)) {
105 8
            return $this->buildExpressionCallback($type, $object, $callback);
106
        }
107
108 2
        throw new \InvalidArgumentException(sprintf('Callback for type "%s" is not a valid callback.', $type));
109
    }
110
111
    /**
112
     * Processes a string expression into an Expression.
113
     *
114
     * @param string $type
115
     * @param mixed  $object
116
     * @param string $callback
117
     *
118
     * @return Expression
119
     */
120 8
    private function buildExpressionCallback($type, $object, $callback)
121
    {
122 8
        $expression = $this->getExpressionLanguage();
123 8
        if (!$expression) {
124
            throw new \RuntimeException('Unable to process an expression without the ExpressionLanguage component.');
125
        }
126
127
        try {
128 8
            $callback = new Expression($callback);
129 8
            $expression->compile($callback, [
130 8
                'object', $this->getExpressionVar($object),
131
            ]);
132
133 6
            return $callback;
134 2
        } catch (SyntaxError $e) {
135 2
            throw new \InvalidArgumentException(sprintf(
136 2
                'Callback for type "%s" is an invalid expression',
137 2
                $type
138 2
            ), $e->getCode(), $e);
139
        }
140
    }
141
142
    /**
143
     * Retreives a cached callback, or creates a new callback if one is not found.
144
     *
145
     * @param string $type
146
     * @param object $object
147
     *
148
     * @return mixed
149
     */
150 15
    private function getCallback($type, $object)
151
    {
152 15
        if (!array_key_exists($type, $this->initialisedCallbacks)) {
153 15
            $this->initialisedCallbacks[$type] = $this->buildCallback($type, $object);
154
        }
155
156 11
        return $this->initialisedCallbacks[$type];
157
    }
158
159
    /**
160
     * Returns the ExpressionLanguage class if it is available.
161
     *
162
     * @return ExpressionLanguage|null
163
     */
164 8
    private function getExpressionLanguage()
165
    {
166 8
        if (null === $this->expressionLanguage) {
167 8
            $this->expressionLanguage = new ExpressionLanguage();
168
        }
169
170 8
        return $this->expressionLanguage;
171
    }
172
173
    /**
174
     * Returns the variable name to be used to access the object when using the ExpressionLanguage
175
     * component to parse and evaluate an expression.
176
     *
177
     * @param mixed $object
178
     *
179
     * @return string
180
     */
181 8
    private function getExpressionVar($object = null)
182
    {
183 8
        if (!is_object($object)) {
184
            return 'object';
185
        }
186
187 8
        $ref = new \ReflectionClass($object);
188
189 8
        return strtolower($ref->getShortName());
190
    }
191
}
192