-
Notifications
You must be signed in to change notification settings - Fork 240
/
Copy pathClassCodeGenerator.php
124 lines (103 loc) · 3.67 KB
/
ClassCodeGenerator.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<?php
/*
* This file is part of the Prophecy.
* (c) Konstantin Kudryashov <ever.zet@gmail.com>
* Marcello Duarte <marcello.duarte@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Prophecy\Doubler\Generator;
use Prophecy\Doubler\Generator\Node\ReturnTypeNode;
use Prophecy\Doubler\Generator\Node\TypeNodeAbstract;
/**
* Class code creator.
* Generates PHP code for specific class node tree.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*/
class ClassCodeGenerator
{
// Used to accept an optional first argument with the deprecated Prophecy\Doubler\Generator\TypeHintReference so careful when adding a new argument in a minor version.
public function __construct()
{
}
/**
* Generates PHP code for class node.
*
* @param string $classname
* @param Node\ClassNode $class
*
* @return string
*/
public function generate($classname, Node\ClassNode $class)
{
$parts = explode('\\', $classname);
$classname = array_pop($parts);
$namespace = implode('\\', $parts);
$code = sprintf("%sclass %s extends \%s implements %s {\n",
$class->isReadOnly() ? 'readonly ': '',
$classname,
$class->getParentClass(),
implode(', ',
array_map(function ($interface) {return '\\'.$interface;}, $class->getInterfaces())
)
);
foreach ($class->getProperties() as $name => $visibility) {
$code .= sprintf("%s \$%s;\n", $visibility, $name);
}
$code .= "\n";
foreach ($class->getMethods() as $method) {
$code .= $this->generateMethod($method)."\n";
}
$code .= "\n}";
return sprintf("namespace %s {\n%s\n}", $namespace, $code);
}
private function generateMethod(Node\MethodNode $method): string
{
$php = sprintf("%s %s function %s%s(%s)%s {\n",
$method->getVisibility(),
$method->isStatic() ? 'static' : '',
$method->returnsReference() ? '&':'',
$method->getName(),
implode(', ', $this->generateArguments($method->getArguments())),
($ret = $this->generateTypes($method->getReturnTypeNode())) ? ': '.$ret : ''
);
$php .= $method->getCode()."\n";
return $php.'}';
}
private function generateTypes(TypeNodeAbstract $typeNode): string
{
if (!$typeNode->getTypes()) {
return '';
}
// When we require PHP 8 we can stop generating ?foo nullables and remove this first block
if ($typeNode->canUseNullShorthand()) {
return sprintf( '?%s', $typeNode->getNonNullTypes()[0]);
} else {
return join('|', $typeNode->getTypes());
}
}
/**
* @param list<Node\ArgumentNode> $arguments
*
* @return list<string>
*/
private function generateArguments(array $arguments): array
{
return array_map(function (Node\ArgumentNode $argument){
$php = $this->generateTypes($argument->getTypeNode());
$php .= ' '.($argument->isPassedByReference() ? '&' : '');
$php .= $argument->isVariadic() ? '...' : '';
$php .= '$'.$argument->getName();
if ($argument->isOptional() && !$argument->isVariadic()) {
$default = $argument->getDefault();
if (is_object($argument->getDefault())) {
$default = null;
}
$php .= ' = '.var_export($default, true);
}
return $php;
}, $arguments);
}
}