Completed
Push — master ( d5fb94...2fc4c0 )
by Vladimir
02:16
created

JailedDocument::offsetExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
/**
4
 * @copyright 2018 Vladimir Jimenez
5
 * @license   https://github.com/stakx-io/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx\Document;
9
10
/**
11
 * A wrapper object to only allow certain functions on the white list to be called and will redirect "jailed" function
12
 * calls to their appropriate jailed calls. This is used in order to limit which functions a user can call from
13
 * templates to prevent unexpected behavior.
14
 */
15
class JailedDocument implements \ArrayAccess, \IteratorAggregate, \JsonSerializable
16
{
17
    /** @var string[] */
18
    private $whiteListFunctions;
19
20
    /** @var string[] */
21
    private $jailedFunctions;
22
23
    /** @var TemplateReadyDocument */
24
    private $object;
25
26
    /** @var array */
27
    private $debugInfo;
28
29
    /**
30
     * JailObject constructor.
31
     *
32
     * @param TemplateReadyDocument $object             the object that will be jailed
33
     * @param array                 $whiteListFunctions a list of function names that can be called
34
     * @param array                 $jailedFunctions    a list of functions that will be redirected to another function
35
     */
36 53
    public function __construct(TemplateReadyDocument &$object, array $whiteListFunctions, array $jailedFunctions = [])
37
    {
38 53
        $this->object = &$object;
39 53
        $this->whiteListFunctions = $whiteListFunctions;
40 53
        $this->jailedFunctions = $jailedFunctions;
41 53
        $this->debugInfo = [];
42 53
    }
43
44 12
    public function __call($name, $arguments)
45
    {
46
        // White listed functions will always be getter functions, so somehow get the name of a possible getter function
47
        // name.
48 12
        $lcName = lcfirst($name);
49 12
        $getFxnCall = ($lcName[0] === '_' || strpos($lcName, 'get') === 0) ? $lcName : sprintf('get%s', ucfirst($name));
50
51
        // Check if our function call is a jailed call, meaning the function should be mapped to special "jailed"
52
        // jailed version of the function call.
53 12
        if (array_key_exists($getFxnCall, $this->jailedFunctions))
54
        {
55 3
            return call_user_func_array([$this->object, $this->jailedFunctions[$getFxnCall]], $arguments);
56
        }
57
58
        // Otherwise, test to see if the function call is in our white list and call it
59 9
        if (in_array($getFxnCall, $this->whiteListFunctions))
60
        {
61 8
            return call_user_func_array([$this->object, $getFxnCall], $arguments);
62
        }
63
64 2
        throw new \BadMethodCallException();
65
    }
66
67 1
    public function __debugInfo()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
68
    {
69 1
        if (!empty($this->debugInfo))
70
        {
71
            return $this->debugInfo;
72
        }
73
74 1
        if ($this->object instanceof FrontMatterDocument)
75
        {
76 1
            $this->debugInfo = $this->object->getFrontMatter(true);
77
        }
78
79 1
        foreach ($this->whiteListFunctions as $function)
80
        {
81 1
            $value = preg_replace('/^(get|is)/', '', $function);
82 1
            $value = lcfirst($value);
83
84
            try
85
            {
86 1
                $this->debugInfo[$value] = call_user_func([$this, $function]);
87
            }
88 1
            catch (\BadMethodCallException $e)
89 1
            {
90
                // Just throw away this information because there's no point in listing an accessible value in this
91
                // object that doesn't actually exist.
92
            }
93
        }
94
95 1
        return $this->debugInfo;
96
    }
97
98 1
    public function __toString()
99
    {
100 1
        @trigger_error("You're casting a JailedDocument into a string, did you really mean to do this?", E_USER_WARNING);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
101
102 1
        return serialize($this->__debugInfo());
103
    }
104
105
    /**
106
     * Check if the jailed object is an instance of a given class.
107
     *
108
     * @param string $class
109
     *
110
     * @return bool
111
     */
112
    public function _coreInstanceOf($class)
113
    {
114
        return ($this->object instanceof $class) || is_subclass_of($this->object, $class);
115
    }
116
117
    ///
118
    // ArrayAccess Implementation
119
    ///
120
121
    /**
122
     * {@inheritdoc}
123
     */
124 22
    public function offsetExists($offset)
125
    {
126 22
        return $this->object->offsetExists($offset);
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132 32
    public function offsetGet($offset)
133
    {
134 32
        return $this->object->offsetGet($offset);
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140
    public function offsetSet($offset, $value)
141
    {
142
        throw new \LogicException('A jailed document is read-only.');
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148
    public function offsetUnset($offset)
149
    {
150
        throw new \LogicException('A jailed document is read-only.');
151
    }
152
153
    ///
154
    // IteratorAggregate implementation
155
    ///
156
157
    /**
158
     * {@inheritdoc}
159
     */
160 1
    public function getIterator()
161
    {
162 1
        return $this->object->getIterator();
163
    }
164
165
    ///
166
    // JsonSerializable implementation
167
    ///
168
169
    /**
170
     * {@inheritdoc}
171
     */
172
    public function jsonSerialize()
173
    {
174
        return $this->object->jsonSerialize();
175
    }
176
}
177