Mutator::parseArgs()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
namespace Sofa\Eloquence\Mutator;
4
5
use ReflectionException;
6
use ReflectionClass;
7
use ReflectionMethod;
8
use Illuminate\Support\Traits\Macroable;
9
use Sofa\Eloquence\Contracts\Mutator as MutatorContract;
10
11
class Mutator implements MutatorContract
12
{
13
    use Macroable;
14
15
    /**
16
     * Mutate value using provided methods.
17
     *
18
     * @param  mixed $value
19
     * @param  string|array $callables
20
     * @return mixed
21
     *
22
     * @throws \LogicException
23
     */
24
    public function mutate($value, $callables)
25
    {
26
        if (!is_array($callables)) {
27
            $callables = explode('|', $callables);
28
        }
29
30
        foreach ($callables as $callable) {
31
            list($callable, $args) = $this->parse(trim($callable));
32
33
            $value = call_user_func_array($callable, array_merge([$value], $args));
34
        }
35
36
        return $value;
37
    }
38
39
    /**
40
     * Parse provided mutator functions.
41
     *
42
     * @param  string $callable
43
     * @return array
44
     *
45
     * @throws \Sofa\Eloquence\Mutator\InvalidCallableException
46
     */
47
    protected function parse($callable)
48
    {
49
        list($callable, $args) = $this->parseArgs($callable);
50
51
        if ($this->isClassMethod($callable)) {
52
            $callable = $this->parseClassMethod($callable);
53
        } elseif ($this->isMutatorMethod($callable)) {
54
            $callable = [$this, $callable];
55
        } elseif (!function_exists($callable)) {
56
            throw new InvalidCallableException("Function [{$callable}] not found.");
57
        }
58
59
        return [$callable, $args];
60
    }
61
62
    /**
63
     * Determine whether callable is a class method.
64
     *
65
     * @param  string  $callable
66
     * @return boolean
67
     */
68
    protected function isClassMethod($callable)
69
    {
70
        return strpos($callable, '@') !== false;
71
    }
72
73
    /**
74
     * Determine whether callable is available on this instance.
75
     *
76
     * @param  string  $callable
77
     * @return boolean
78
     */
79
    protected function isMutatorMethod($callable)
80
    {
81
        return method_exists($this, $callable) || static::hasMacro($callable);
82
    }
83
84
    /**
85
     * Split provided string into callable and arguments.
86
     *
87
     * @param  string $callable
88
     * @return array
89
     */
90
    protected function parseArgs($callable)
91
    {
92
        $args = [];
93
94
        if (strpos($callable, ':') !== false) {
95
            list($callable, $argsString) = explode(':', $callable);
96
97
            $args = explode(',', $argsString);
98
        }
99
100
        return [$callable, $args];
101
    }
102
103
    /**
104
     * Extract and validate class method.
105
     *
106
     * @param  string   $userCallable
107
     * @return callable
108
     *
109
     * @throws \Sofa\Eloquence\Mutator\InvalidCallableException
110
     */
111
    protected function parseClassMethod($userCallable)
112
    {
113
        list($class) = explode('@', $userCallable);
114
115
        $callable = str_replace('@', '::', $userCallable);
116
117
        try {
118
            $method = new ReflectionMethod($callable);
119
120
            $class = new ReflectionClass($class);
121
        } catch (ReflectionException $e) {
122
            throw new InvalidCallableException($e->getMessage());
123
        }
124
125
        return ($method->isStatic()) ? $callable : $this->getInstanceMethod($class, $method);
126
    }
127
128
    /**
129
     * Get instance callable.
130
     *
131
     * @param  \ReflectionMethod  $method
132
     * @return callable
133
     *
134
     * @throws \Sofa\Eloquence\Mutator\InvalidCallableException
135
     */
136
    protected function getInstanceMethod(ReflectionClass $class, ReflectionMethod $method)
137
    {
138
        if (!$method->isPublic()) {
139
            throw new InvalidCallableException("Instance method [{$class}@{$method->getName()}] is not public.");
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
140
        }
141
142
        if (!$this->canInstantiate($class)) {
143
            throw new InvalidCallableException("Can't instantiate class [{$class->getName()}].");
0 ignored issues
show
Bug introduced by
Consider using $class->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
144
        }
145
146
        return [$class->newInstance(), $method->getName()];
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
147
    }
148
149
    /**
150
     * Determine whether instance can be instantiated.
151
     *
152
     * @param  \ReflectionClass  $class
153
     * @return boolean
154
     */
155
    protected function canInstantiate(ReflectionClass $class)
156
    {
157
        if (!$class->isInstantiable()) {
158
            return false;
159
        }
160
161
        $constructor = $class->getConstructor();
162
163
        return is_null($constructor) || 0 === $constructor->getNumberOfRequiredParameters();
164
    }
165
}
166