MagicMethodTypeHintsPass::getMagicMethods()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 1
crap 2
1
<?php
2
/**
3
 * Mockery
4
 *
5
 * LICENSE
6
 *
7
 * This source file is subject to the new BSD license that is bundled
8
 * with this package in the file LICENSE.txt.
9
 * It is also available through the world-wide-web at this URL:
10
 * http://github.com/padraic/mockery/blob/master/LICENSE
11
 * If you did not receive a copy of the license and are unable to
12
 * obtain it through the world-wide-web, please send an email
13
 * to [email protected] so we can send you a copy immediately.
14
 *
15
 * @category   Mockery
16
 * @package    Mockery
17
 * @copyright  Copyright (c) 2010 Pádraic Brady (http://blog.astrumfutura.com)
18
 * @license    http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
19
 */
20
21
namespace Mockery\Generator\StringManipulation\Pass;
22
23
use Mockery\Generator\MockConfiguration;
24
use Mockery\Generator\TargetClassInterface;
25
use Mockery\Generator\Method;
26
27
class MagicMethodTypeHintsPass implements Pass
28
{
29
    /**
30
     * @var array $mockMagicMethods
31
     */
32
    private $mockMagicMethods = array(
33
        '__construct',
34
        '__destruct',
35
        '__call',
36
        '__callStatic',
37
        '__get',
38
        '__set',
39
        '__isset',
40
        '__unset',
41
        '__sleep',
42
        '__wakeup',
43
        '__toString',
44
        '__invoke',
45
        '__set_state',
46
        '__clone',
47
        '__debugInfo'
48
    );
49
50
    /**
51
     * Apply implementation.
52
     *
53
     * @param $code
54
     * @param MockConfiguration $config
55
     * @return string
56
     */
57 397
    public function apply($code, MockConfiguration $config)
58
    {
59 397
        $magicMethods = $this->getMagicMethods($config->getTargetClass());
60 397
        foreach ($config->getTargetInterfaces() as $interface) {
0 ignored issues
show
Bug introduced by
The expression $config->getTargetInterfaces() of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
61 19
            $magicMethods = array_merge($magicMethods, $this->getMagicMethods($interface));
62 397
        }
63
64 397
        foreach ($magicMethods as $method) {
65 46
            $code = $this->applyMagicTypeHints($code, $method);
66 397
        }
67
68 397
        return $code;
69
    }
70
71
    /**
72
     * Returns the magic methods within the
73
     * passed DefinedTargetClass.
74
     *
75
     * @param TargetClassInterface $class
76
     * @return array
77
     */
78 397
    public function getMagicMethods(
79
        TargetClassInterface $class = null
80
    ) {
81 397
        if (is_null($class)) {
82 28
            return array();
83
        }
84 384
        return array_filter($class->getMethods(), function (Method $method) {
85 121
            return in_array($method->getName(), $this->mockMagicMethods);
0 ignored issues
show
Documentation Bug introduced by
The method getName does not exist on object<Mockery\Generator\Method>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
86 384
        });
87
    }
88
89
    /**
90
     * Applies type hints of magic methods from
91
     * class to the passed code.
92
     *
93
     * @param $code
94
     * @param Method $method
95
     * @return string
96
     */
97 46
    private function applyMagicTypeHints($code, Method $method)
98
    {
99 46
        if ($this->isMethodWithinCode($code, $method)) {
100 31
            $namedParameters = $this->getOriginalParameters(
101 31
                $code,
102
                $method
103 31
            );
104 31
            $code = preg_replace(
105 31
                $this->getDeclarationRegex($method->getName()),
0 ignored issues
show
Documentation Bug introduced by
The method getName does not exist on object<Mockery\Generator\Method>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
106 31
                $this->getMethodDeclaration($method, $namedParameters),
107
                $code
108 31
            );
109 31
        }
110 46
        return $code;
111
    }
112
113
    /**
114
     * Checks if the method is declared withing code.
115
     *
116
     * @param $code
117
     * @param Method $method
118
     * @return boolean
119
     */
120 46
    private function isMethodWithinCode($code, Method $method)
121
    {
122 46
        return preg_match(
123 46
            $this->getDeclarationRegex($method->getName()),
0 ignored issues
show
Documentation Bug introduced by
The method getName does not exist on object<Mockery\Generator\Method>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
124
            $code
125 46
        ) == 1;
126
    }
127
128
    /**
129
     * Returns the method original parameters, as they're
130
     * described in the $code string.
131
     *
132
     * @param $code
133
     * @param Method $method
134
     * @return array
135
     */
136 31
    private function getOriginalParameters($code, Method $method)
137
    {
138 31
        $methodName = $method->getName();
0 ignored issues
show
Documentation Bug introduced by
The method getName does not exist on object<Mockery\Generator\Method>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
Unused Code introduced by
$methodName is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
139 31
        $matches = [];
140 31
        $parameterMatches = [];
141
142 31
        preg_match(
143 31
            $this->getDeclarationRegex($method->getName()),
0 ignored issues
show
Documentation Bug introduced by
The method getName does not exist on object<Mockery\Generator\Method>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
144 31
            $code,
145
            $matches
146 31
        );
147
148 31
        if (count($matches) > 0) {
149 31
            preg_match_all(
150 31
                '/(?<=\$)(\w+)+/i',
151 31
                $matches[0],
152
                $parameterMatches
153 31
            );
154 31
        }
155
156 31
        $groupMatches = end($parameterMatches);
157 31
        $parameterNames = is_array($groupMatches) ?
158 31
            $groupMatches                         :
159 31
            array($groupMatches);
160
161 31
        return $parameterNames;
162
    }
163
164
    /**
165
     * Gets the declaration code, as a string, for the passed method.
166
     *
167
     * @param Method $method
168
     * @param array  $namedParameters
169
     * @return string
170
     */
171 31
    private function getMethodDeclaration(
172
        Method $method,
173
        array $namedParameters
174
    ) {
175 31
        $declaration = 'public';
176 31
        $declaration .= $method->isStatic() ? ' static' : '';
0 ignored issues
show
Documentation Bug introduced by
The method isStatic does not exist on object<Mockery\Generator\Method>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
177 31
        $declaration .= ' function '.$method->getName().'(';
0 ignored issues
show
Documentation Bug introduced by
The method getName does not exist on object<Mockery\Generator\Method>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
178
179 31
        foreach ($method->getParameters() as $index => $parameter) {
180 9
            $declaration .= $parameter->getTypeHintAsString().' ';
181 9
            $name = isset($namedParameters[$index]) ?
182 9
                $namedParameters[$index]            :
183 9
                $parameter->getName();
184 9
            $declaration .= '$'.$name;
185 9
            $declaration .= ',';
186 31
        }
187 31
        $declaration = rtrim($declaration, ',');
188 31
        $declaration .= ') ';
189
190 31
        $returnType = $method->getReturnType();
191 31
        if (!empty($returnType)) {
192
            $declaration .= ': '.$returnType;
193
        }
194
195 31
        return $declaration;
196
    }
197
198
    /**
199
     * Returns a regex string used to match the
200
     * declaration of some method.
201
     *
202
     * @param string $methodName
203
     * @return string
204
     */
205 46
    private function getDeclarationRegex($methodName)
206
    {
207 46
        $method = strtolower($methodName);
0 ignored issues
show
Unused Code introduced by
$method is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
208 46
        return "/public\s+(?:static\s+)?function\s+$methodName\s*\(.*\)\s*(?=\{)/i";
209
    }
210
}
211