Completed
Pull Request — master (#59)
by
unknown
36:10
created

HydratorFunctionalTest::testDisabledMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 1
eloc 5
nc 1
nop 0
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license.
17
 */
18
19
declare(strict_types=1);
20
21
namespace GeneratedHydratorTest\Functional;
22
23
use CodeGenerationUtils\GeneratorStrategy\EvaluatingGeneratorStrategy;
24
use CodeGenerationUtils\Inflector\ClassNameInflectorInterface;
25
use CodeGenerationUtils\Inflector\Util\UniqueIdentifierGenerator;
26
use GeneratedHydrator\Configuration;
27
use GeneratedHydratorTestAsset\BaseClass;
28
use GeneratedHydratorTestAsset\ClassWithMixedProperties;
29
use GeneratedHydratorTestAsset\ClassWithPrivateProperties;
30
use GeneratedHydratorTestAsset\ClassWithProtectedProperties;
31
use GeneratedHydratorTestAsset\ClassWithPublicProperties;
32
use GeneratedHydratorTestAsset\ClassWithStaticProperties;
33
use GeneratedHydratorTestAsset\EmptyClass;
34
use GeneratedHydratorTestAsset\HydratedObject;
35
use PHPUnit_Framework_TestCase;
36
use ReflectionClass;
37
use stdClass;
38
use Zend\Hydrator\HydratorInterface;
39
40
/**
41
 * Tests for {@see \GeneratedHydrator\ClassGenerator\HydratorGenerator} produced objects
42
 *
43
 * @author Marco Pivetta <[email protected]>
44
 * @license MIT
45
 *
46
 * @group Functional
47
 */
48
class HydratorFunctionalTest extends PHPUnit_Framework_TestCase
49
{
50
    /**
51
     * @dataProvider getHydratorClasses
52
     *
53
     * @param object $instance
54
     */
55
    public function testHydrator($instance)
56
    {
57
        $reflection  = new ReflectionClass($instance);
58
        $properties  = $reflection->getProperties();
59
        $initialData = array();
60
        $newData     = array();
61
62
        foreach ($properties as $property) {
63
            if ($property->isStatic()) {
64
                continue;
65
            }
66
67
            $propertyName = $property->getName();
68
69
            $property->setAccessible(true);
70
            $initialData[$propertyName] = $property->getValue($instance);
71
            $newData[$propertyName]     = $property->getName() . '__new__value';
72
        }
73
74
        $generatedClass = $this->generateHydrator($instance);
75
76
        self::assertSame($initialData, $generatedClass->extract($instance));
77
        self::assertSame($instance, $generatedClass->hydrate($newData, $instance));
78
79
        $inspectionData = array();
80
81
        foreach ($properties as $property) {
82
            if ($property->isStatic()) {
83
                continue;
84
            }
85
86
            $propertyName = $property->getName();
87
88
            $property->setAccessible(true);
89
            $inspectionData[$propertyName] = $property->getValue($instance);
90
        }
91
92
        self::assertSame($inspectionData, $newData);
93
        self::assertSame($inspectionData, $generatedClass->extract($instance));
94
    }
95
96
    /**
97
     * @return array
98
     */
99
    public function getHydratorClasses() : array
100
    {
101
        return [
102
            [new stdClass()],
103
            [new EmptyClass()],
104
            [new HydratedObject()],
105
            [new BaseClass()],
106
            [new ClassWithPublicProperties()],
107
            [new ClassWithProtectedProperties()],
108
            [new ClassWithPrivateProperties()],
109
            [new ClassWithMixedProperties()],
110
            [new ClassWithStaticProperties()],
111
        ];
112
    }
113
114
    /**
115
     * Generates a hydrator for the given class name, and retrieves its class name
116
     *
117
     * @param object $instance
118
     *
119
     * @return HydratorInterface
120
     */
121
    private function generateHydrator($instance) : HydratorInterface
122
    {
123
        $parentClassName    = get_class($instance);
124
        $generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo');
125
        $config             = new Configuration($parentClassName);
126
        /* @var $inflector ClassNameInflectorInterface|\PHPUnit_Framework_MockObject_MockObject */
127
        $inflector          = $this->createMock(ClassNameInflectorInterface::class);
128
129
        $inflector
0 ignored issues
show
Bug introduced by
The method expects does only exist in PHPUnit_Framework_MockObject_MockObject, but not in CodeGenerationUtils\Infl...sNameInflectorInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
130
            ->expects(self::any())
131
            ->method('getGeneratedClassName')
132
            ->with($parentClassName)
133
            ->will(self::returnValue($generatedClassName));
134
        $inflector
135
            ->expects(self::any())
136
            ->method('getUserClassName')
137
            ->with($parentClassName)
138
            ->will(self::returnValue($parentClassName));
139
140
        $config->setClassNameInflector($inflector);
0 ignored issues
show
Bug introduced by
It seems like $inflector defined by $this->createMock(\CodeG...lectorInterface::class) on line 127 can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, GeneratedHydrator\Config...setClassNameInflector() does only seem to accept object<CodeGenerationUti...NameInflectorInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
141
        $config->setGeneratorStrategy(new EvaluatingGeneratorStrategy());
142
143
        $generatedClass = $config->createFactory()->getHydratorClass();
144
145
        return new $generatedClass;
146
    }
147
}
148