Completed
Push — master ( 6962e4...5fca22 )
by Emily
02:24
created

GenericCompositeGenerator::generateMethodCode()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 27
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 20
cts 20
cp 1
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 22
nc 4
nop 1
crap 3
1
<?php
2
/**
3
 * This file is part of the Composite Utils package.
4
 *
5
 * (c) Emily Shepherd <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the
8
 * LICENSE.md file that was distributed with this source code.
9
 *
10
 * @package spaark/composite-utils
11
 * @author Emily Shepherd <[email protected]>
12
 * @license MIT
13
 */
14
15
namespace Spaark\CompositeUtils\Factory;
16
17
use Spaark\CompositeUtils\Model\Reflection\ReflectionComposite;
18
use Spaark\CompositeUtils\Model\Reflection\ReflectionMethod;
19
use Spaark\CompositeUtils\Model\Reflection\Type\BooleanType;
20
use Spaark\CompositeUtils\Model\Reflection\Type\CollectionType;
21
use Spaark\CompositeUtils\Model\Reflection\Type\IntegerType;
22
use Spaark\CompositeUtils\Model\Reflection\Type\MixedType;
23
use Spaark\CompositeUtils\Model\Reflection\Type\ObjectType;
24
use Spaark\CompositeUtils\Model\Reflection\Type\StringType;
25
use Spaark\CompositeUtils\Model\Reflection\Type\GenericType;
26
use Spaark\CompositeUtils\Model\Generic\GenericContext;
27
use Spaark\CompositeUtils\Model\ClassName;
28
use Spaark\CompositeUtils\Service\RawPropertyAccessor;
29
use Spaark\CompositeUtils\Service\GenericNameProvider;
30
use Spaark\CompositeUtils\Traits\AutoConstructPropertyAccessTrait;
31
use Spaark\CompositeUtils\Traits\GenericTrait;
32
use Spaark\CompositeUtils\Traits\Generic;
33
34
/**
35
 * Generates the code for a generic class
36
 */
37
class GenericCompositeGenerator
38
{
39
    use AutoConstructPropertyAccessTrait;
40
41
    /**
42
     * @var ReflectionComposite
43
     * @construct required
44
     */
45
    protected $reflect;
46
47
    /**
48
     * @var GenericNameProvider
49
     */
50
    protected $nameProvider;
51
52
    /**
53
     * @var ?ClassName
54
     * @readable
55
     */
56
    protected $generatedClassName;
57
58
    /**
59
     * Creates an ObjectType from the given list of generics
60
     *
61
     * @param AbstractType[] $generics
62
     */
63 2
    private function createObject(...$generics) : ObjectType
64
    {
65 2
        $object = new ObjectType($this->reflect->classname);
66 2
        $i = 0;
67
68 2
        foreach ($this->reflect->generics as $name => $value)
0 ignored issues
show
Documentation introduced by
The property $generics is declared protected in Spaark\CompositeUtils\Mo...ion\ReflectionComposite. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
69
        {
70 2
            $object->generics[] = $generics[$i++] ?? $value;
0 ignored issues
show
Documentation introduced by
The property $generics is declared protected in Spaark\CompositeUtils\Mo...lection\Type\ObjectType. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
71
        }
72
73 2
        return $object;
74
    }
75
76
    /**
77
     * Generate class code for the given generics
78
     *
79
     * @param AbstractType[] $generics
80
     * @return string
81
     */
82 2
    public function generateClassCode(...$generics) : string
83
    {
84 2
        $object = $this->createObject(...$generics);
85 2
        $this->nameProvider = new GenericNameProvider
86
        (
87 2
            new GenericContext($object, $this->reflect)
88
        );
89 2
        $class = $this->nameProvider->inferName($object);
90 2
        $this->generatedClassName = $class;
91 2
        $originalClass = $this->reflect->classname;
92 2
        $i = 0;
0 ignored issues
show
Unused Code introduced by
$i 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...
93
94
        $code =
95 2
              'namespace ' . $class->namespace . ';'
96 2
            . 'class ' . $class->classname . ' '
97 2
            .     'extends \\' . $originalClass . ' '
98 2
            .     'implements \\' . Generic::class
99 2
            . '{'
100 2
            . 'use \\' . GenericTrait::class . ';';
101
102 2
        foreach ($this->reflect->methods as $method)
103
        {
104 2
            $code .= $this->generateMethodCode($method);
105
        }
106
107 2
        $code .= '}';
108
109 2
        return $code;
110
    }
111
112
    /**
113
     * Generates the method code for the current class being generated
114
     *
115
     * @param ReflectionMethod $method
116
     * @return string
117
     */
118 2
    public function generateMethodCode(ReflectionMethod $method)
119
        : string
120
    {
121 2
        $params = [];
122 2
        $newParams = [];
123 2
        $paramNames = [];
124 2
        foreach ($method->parameters as $i => $param)
125
        {
126 2
            $paramNames[] = $name = ' $' . $param->name;
127 2
            $params[] = $method->nativeParameters[$i] . $name;
0 ignored issues
show
Documentation introduced by
The property $nativeParameters is declared protected in Spaark\CompositeUtils\Mo...ection\ReflectionMethod. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
128 2
            $newParams[] =
129 2
                $this->nameProvider->inferName($param->type) . $name;
130
        }
131
132
        return
133 2
              ($method->scope === 'static' ? 'static ' : '')
134 2
            . 'function ' . $method->name
135 2
            . '(' . implode(',', $params) . '){'
136 2
            . '__generic_' . $method->name
137 2
            . '(' . implode(',', $paramNames) . ');}'
138 2
            . "\n"
139 2
            . 'function __generic_' . $method->name
140 2
            . '(' . implode(',', $newParams) . '){'
141 2
            . 'parent::' . $method->name
142 2
            . '(' . implode(',', $paramNames) . ');}'
143 2
            . "\n";
144
    }
145
146
    public function createClass(...$generics)
147
    {
148
        eval($this->generateClassCode(...$generics));
149
    }
150
}
151