1 | <?php |
||
14 | class Generator |
||
15 | { |
||
16 | /** |
||
17 | * @var BuilderFactory |
||
18 | */ |
||
19 | private $factory; |
||
20 | |||
21 | /** |
||
22 | * @var PrettyPrinterAbstract |
||
23 | */ |
||
24 | private $printer; |
||
25 | |||
26 | /** |
||
27 | * @var System |
||
28 | */ |
||
29 | private $system; |
||
30 | |||
31 | 1 | public function __construct( |
|
40 | |||
41 | public function generate(ObjectSchema $schema, string $className, string $baseClass = ''): array |
||
42 | { |
||
43 | $classes = []; |
||
44 | |||
45 | $className = new Name($className); |
||
46 | // Start the class AST definition |
||
47 | $namespace = $this->factory->namespace($className->slice(0, -1)->toString()); |
||
48 | $class = $this->factory->class($className->getLast()); |
||
49 | |||
50 | if ($baseClass) { |
||
51 | $baseClassName = new Name($baseClass); |
||
52 | // Import the base class with a "use" statement |
||
53 | $namespace->addStmt($this->factory->use($baseClassName)); |
||
54 | // Make the class extend the base class |
||
55 | $class->extend($baseClassName->getLast()); |
||
56 | } |
||
57 | |||
58 | foreach ($schema->properties() as $name => $property) { |
||
59 | $typeHint = $property->phpType(); |
||
60 | |||
61 | if ($property->isObject()) { |
||
62 | // Create a new class for this property |
||
63 | $nextClass = Name::concat($className, ucfirst($name)); |
||
64 | $typeHint = '\\' . $nextClass->toString(); |
||
65 | $classes = array_merge($classes, $this->generate( |
||
66 | $property, |
||
|
|||
67 | $nextClass->toString(), |
||
68 | $baseClass |
||
69 | )); |
||
70 | } elseif ($property->isArray() && $property->hasItems() && $property->items()->isObject()) { |
||
71 | // Create a new class for this array of properties |
||
72 | $nextClass = Name::concat($className, ucfirst(singular($name))); |
||
73 | $typeHint = '\\' . $nextClass->toString() . '[]'; |
||
74 | $classes = array_merge($classes, $this->generate( |
||
75 | $property->items(), |
||
76 | $nextClass->toString(), |
||
77 | $baseClass |
||
78 | )); |
||
79 | } elseif (!$schema->isRequired($name) && !$property->isNull()) { |
||
80 | $typeHint = "$typeHint|null"; |
||
81 | } |
||
82 | |||
83 | // Add a property declaration to the class |
||
84 | $class->addStmt( |
||
85 | $this->factory->property($name) |
||
86 | ->makePublic() |
||
87 | ->setDocComment("/**\n * @var $typeHint\n */") |
||
88 | ); |
||
89 | } |
||
90 | |||
91 | // Add the class declaration to the namespace |
||
92 | $namespace->addStmt($class); |
||
93 | |||
94 | $classes[$className->toString()] = $namespace->getNode(); |
||
95 | |||
96 | return $classes; |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * @param Ns[] $classes |
||
101 | * @return string[] |
||
102 | */ |
||
103 | public function write(array $classes, string $rootDirectory, string $rootNamespace = ''): array |
||
104 | { |
||
105 | $rootDirectory = rtrim($rootDirectory, '/'); |
||
106 | |||
107 | return array_map( |
||
108 | function (Ns $node) use ($rootDirectory, $rootNamespace): string { |
||
109 | // Remove the root (PSR-4) namespace and convert to a path |
||
110 | $directory = str_replace($rootNamespace, '', $node->name->toString()); |
||
111 | $directory = trim(str_replace('\\', '/', $directory), '/'); |
||
112 | $directory = rtrim("$rootDirectory/$directory", '/'); |
||
113 | // Grab the class name from AST |
||
114 | $class = $this->classNode($node->stmts)->name; |
||
115 | |||
116 | $path = "$directory/$class.php"; |
||
117 | $code = $this->printer->prettyPrintFile([$node]); |
||
118 | |||
119 | $this->system->writeFile($path, $code); |
||
120 | |||
121 | return $path; |
||
122 | }, |
||
123 | $classes |
||
124 | ); |
||
125 | } |
||
126 | |||
127 | 1 | protected function defaultBuilder(): BuilderFactory |
|
132 | |||
133 | 1 | protected function defaultPrinter(): PrettyPrinterAbstract |
|
139 | |||
140 | 1 | protected function defaultSystem(): System |
|
144 | |||
145 | private function classNode(array $stmts): Cls |
||
159 | } |
||
160 |
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.
Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.