Completed
Push — master ( 67e168...53226a )
by Jean
02:28
created

DeferredCallChain   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 24
eloc 61
dl 0
loc 187
rs 10
c 4
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A offsetSet() 0 4 1
A offsetGet() 0 7 1
A __toString() 0 19 3
C __invoke() 0 51 13
A __call() 0 8 1
A offsetExists() 0 4 1
A offsetUnset() 0 4 1
A jsonSerialize() 0 3 1
A __construct() 0 4 2
1
<?php
2
/**
3
 * DeferredCallChain
4
 *
5
 * @package php-deferred-callchain
6
 * @author  Jean Claveau
7
 */
8
namespace JClaveau\Async;
9
use       JClaveau\Async\Exceptions\BadTargetClassException;
10
use       JClaveau\Async\Exceptions\BadTargetTypeException;
11
use       JClaveau\Async\Exceptions\UndefinedTargetClassException;
12
use       JClaveau\Async\Exceptions\BadTargetInterfaceException;
13
use       BadMethodCallException;
14
15
/**
16
 * This class stores an arbitrary stack of calls (methods or array entries access)
17
 * that will be callable on any future variable.
18
 */
19
class DeferredCallChain implements \JsonSerializable, \ArrayAccess
20
{
21
    use \JClaveau\Traits\Fluent\New_;
22
    
23
    /** @var array $stack The stack of deferred calls */
24
    protected $stack = [];
25
26
    /** @var mixed $expectedTarget The stack of deferred calls */
27
    protected $expectedTarget;
28
29
    /**
30
     * Constructor 
31
     * 
32
     * @param string $key The entry to acces
33
     */
34
    public function __construct($class_type_or_instance=null)
35
    {
36
        if ($class_type_or_instance) {
37
            $this->expectedTarget = $class_type_or_instance;
38
        }
39
    }
40
41
    /**
42
     * ArrayAccess interface
43
     *
44
     * @param string $key The entry to acces
45
     */
46
    public function &offsetGet($key)
47
    {
48
        $this->stack[] = [
49
            'entry' => $key,
50
        ];
51
52
        return $this;
53
    }
54
55
    /**
56
     * Stores any call in the the stack.
57
     *
58
     * @param  string $method
59
     * @param  array  $arguments
60
     *
61
     * @return $this
62
     */
63
    public final function __call($method, array $arguments)
64
    {
65
        $this->stack[] = [
66
            'method'    => $method,
67
            'arguments' => $arguments,
68
        ];
69
70
        return $this;
71
    }
72
73
    /**
74
     * For implementing JsonSerializable interface.
75
     *
76
     * @see https://secure.php.net/manual/en/jsonserializable.jsonserialize.php
77
     */
78
    public function jsonSerialize()
79
    {
80
        return $this->stack;
81
    }
82
83
    /**
84
     * Outputs the PHP code producing the current call chain while it's casted
85
     * as a string.
86
     *
87
     * @return string The PHP code corresponding to this call chain
88
     */
89
    public function __toString()
90
    {
91
        $string = '(new ' . get_called_class() . ')';
92
93
        foreach ($this->stack as $i => $call) {
94
            if (isset($call['method'])) {
95
                $string .= '->';
96
                $string .= $call['method'].'(';
97
                $string .= implode(', ', array_map(function($argument) {
98
                    return var_export($argument, true);
99
                }, $call['arguments']));
100
                $string .= ')';
101
            }
102
            else {
103
                $string .= '[' . var_export($call['entry'], true) . ']';
104
            }
105
        }
106
107
        return $string;
108
    }
109
110
    /**
111
     * Invoking the instance produces the call of the stack
112
     *
113
     * @param  $target The target to apply the callchain on
0 ignored issues
show
Bug introduced by
The type JClaveau\Async\The was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
114
     * @return The value returned once the call chain is called uppon $target
115
     */
116
    public function __invoke($target=null)
117
    {
118
        if (is_object($this->expectedTarget)) {
119
            if ($target) {
120
                throw new TargetAlreadyDefinedException($this, $this->expectedTarget, $target);
0 ignored issues
show
Bug introduced by
The type JClaveau\Async\TargetAlreadyDefinedException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
121
            }
122
            
123
            $out = $this->expectedTarget;
124
        }
125
        elseif (is_string($this->expectedTarget)) {
126
            if (class_exists($this->expectedTarget)) {
127
                if (! $target instanceof $this->expectedTarget) {
128
                    throw new BadTargetClassException($this, $this->expectedTarget, $target);
129
                }
130
            }
131
            elseif (interface_exists($this->expectedTarget)) {
132
                if (! $target instanceof $this->expectedTarget) {
133
                    throw new BadTargetInterfaceException($this, $this->expectedTarget, $target);
134
                }
135
            }
136
            elseif (type_exists($this->expectedTarget)) {
137
                if (gettype($target) != $this->expectedTarget) {
138
                    throw new BadTargetTypeException($this, $this->expectedTarget, $target);
139
                }
140
            }
141
            else {
142
                throw new UndefinedTargetClassException($this, $this->expectedTarget);
143
            }
144
            
145
            $out = $target;
146
        }
147
        else {
148
            $out = $target;
149
        }
150
        
151
        foreach ($this->stack as $i => $call) {
152
            try {
153
                if (isset($call['method'])) {
154
                    $out = call_user_func_array([$out, $call['method']], $call['arguments']);
155
                }
156
                else {
157
                    $out = $out[ $call['entry'] ];
158
                }
159
            }
160
            catch (\Exception $e) {
161
                // Throw $e with the good stack (usage exception)
162
                throw $e;
163
            }
164
        }
165
166
        return $out;
167
    }
168
169
    /**
170
     * Unused part of the ArrayAccess interface
171
     *
172
     * @param  $offset
173
     * @param  $value
174
     * @throws \BadMethodCallException
175
     */
176
    public function offsetSet($offset, $value)
177
    {
178
        throw new BadMethodCallException(
179
            "not implemented"
180
        );
181
    }
182
183
    /**
184
     * Unused part of the ArrayAccess interface
185
     *
186
     * @param  $offset
187
     * @throws \BadMethodCallException
188
     */
189
    public function offsetExists($offset)
190
    {
191
        throw new BadMethodCallException(
192
            "not implemented"
193
        );
194
    }
195
196
    /**
197
     * Unused part of the ArrayAccess interface
198
     *
199
     * @param  $offset
200
     * @throws \BadMethodCallException
201
     */
202
    public function offsetUnset($offset)
203
    {
204
        throw new BadMethodCallException(
205
            "not implemented"
206
        );
207
    }
208
209
    /**/
210
}
211