Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
99.01% covered (success)
99.01%
100 / 101
75.00% covered (success)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
CallableObject
99.01% covered (success)
99.01%
100 / 101
75.00% covered (success)
75.00%
3 / 4
46
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 prepare
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
1 / 1
16
 prepareParameters
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
7
 call
98.04% covered (success)
98.04%
50 / 51
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Utils;
15
16/**
17 * Pop utils callable object class
18 *
19 * @category   Pop
20 * @package    Pop\Utils
21 * @author     Nick Sagona, III <dev@nolainteractive.com>
22 * @copyright  Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
23 * @license    http://www.popphp.org/license     New BSD License
24 * @version    1.3.0
25 */
26class CallableObject extends AbstractCallable
27{
28
29    /**
30     * Constructor
31     *
32     * Instantiate the callable object
33     *
34     * @param  mixed $callable
35     * @param  mixed $parameters
36     */
37    public function __construct($callable, $parameters = null)
38    {
39        $this->setCallable($callable);
40        if (null !== $parameters) {
41            if (is_array($parameters)) {
42                $this->setParameters($parameters);
43            } else {
44                $this->addParameter($parameters);
45            }
46        }
47    }
48
49    /**
50     * Prepare object for call
51     *
52     * @return CallableObject
53     */
54    public function prepare()
55    {
56        $class  = null;
57        $method = null;
58
59        if ($this->callable instanceof \Closure) {
60            $this->callableType = self::CLOSURE;
61        } else if (is_object($this->callable)) {
62            $this->callableType = self::OBJECT;
63        } else if (is_string($this->callable)) {
64            if (strpos($this->callable, '::') !== false) {
65                $this->callableType = self::STATIC_CALL;
66                [$class, $method]   = explode('::', $this->callable);
67            } else if (strpos($this->callable, '->') !== false) {
68                $this->callableType = self::INSTANCE_CALL;
69                [$class, $method]   = explode('->', $this->callable);
70            } else if (substr($this->callable, 0, 4) == 'new ') {
71                $this->callableType = self::NEW_OBJECT;
72                $class = substr($this->callable, 4);
73            } else if (class_exists($this->callable)) {
74                $this->callableType = self::CONSTRUCTOR_CALL;
75            } else if (function_exists($this->callable)) {
76                $this->callableType = self::FUNCTION;
77            }
78        } else if (is_callable($this->callable)) {
79            $this->callableType = self::IS_CALLABLE;
80        }
81
82        if (null !== $class) {
83            if (!class_exists($class)) {
84                throw new Exception("Error: The class '" . $class . "' does not exist.");
85            }
86            if (null !== $method) {
87                if (!method_exists($class, $method)) {
88                    throw new Exception("Error: The method '" . $method  . "' does not exist in the class '" . $class . "'.");
89                }
90            }
91        }
92
93        if (null === $this->callableType) {
94            throw new Exception('Error: Unable to prepare the callable object for execution.');
95        } else if (!empty($this->parameters)) {
96            $this->callableType .= '_PARAMS';
97        }
98
99        $this->class  = $class;
100        $this->method = $method;
101
102        return $this;
103    }
104
105    /**
106     * Prepare parameters
107     *
108     * @return CallableObject
109     */
110    public function prepareParameters()
111    {
112        foreach ($this->parameters as $key => $value) {
113            if ($value instanceof self) {
114                $this->parameters[$key] = $value->call();
115            } else if (is_callable($value)) {
116                $this->parameters[$key] = call_user_func($value);
117            } else if (is_array($value) && isset($value[0]) && is_callable($value[0])) {
118                $callable = $value[0];
119                unset($value[0]);
120                $this->parameters[$key] = call_user_func_array($callable, array_values($value));
121            }
122        }
123
124        return $this;
125    }
126
127    /**
128     * Execute the callable
129     *
130     * @param  mixed $parameters
131     * @return mixed
132     */
133    public function call($parameters = null)
134    {
135        if (null !== $parameters) {
136            if (!is_array($parameters)) {
137                $this->addParameter($parameters);
138            } else {
139                $this->addParameters($parameters);
140            }
141        }
142
143        if (null === $this->callableType) {
144            $this->prepare();
145        }
146
147        $this->prepareParameters();
148
149        $result = null;
150
151        switch ($this->callableType) {
152            case self::FUNCTION:
153            case self::CLOSURE:
154            case self::STATIC_CALL:
155            case self::IS_CALLABLE:
156                $result = call_user_func($this->callable);
157                break;
158            case self::FUNCTION_PARAMS:
159            case self::CLOSURE_PARAMS:
160            case self::STATIC_CALL_PARAMS:
161            case self::IS_CALLABLE_PARAMS:
162                $result = call_user_func_array($this->callable, array_values($this->parameters));
163                break;
164            case self::INSTANCE_CALL:
165                $class  = $this->class;
166                $object = (!empty($this->constructorParams)) ?
167                    (new \ReflectionClass($class))->newInstanceArgs($this->constructorParams) :
168                    new $class();
169                $result = call_user_func([$object, $this->method]);
170                break;
171            case self::INSTANCE_CALL_PARAMS:
172                $class  = $this->class;
173                $object = (!empty($this->constructorParams)) ?
174                    (new \ReflectionClass($class))->newInstanceArgs($this->constructorParams) :
175                    new $class();
176                $result = call_user_func_array([$object, $this->method], array_values($this->parameters));
177                break;
178            case self::CONSTRUCTOR_CALL:
179                $class  = $this->callable;
180                $result = new $class();
181                break;
182            case self::CONSTRUCTOR_CALL_PARAMS:
183                $result = (new \ReflectionClass($this->callable))->newInstanceArgs($this->parameters);
184                break;
185            case self::NEW_OBJECT:
186                $class  = $this->class;
187                $result = new $class();
188                break;
189            case self::OBJECT:
190                $result = $this->callable;
191                break;
192        }
193
194        $this->wasCalled = true;
195
196        return $result;
197    }
198
199}