1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\PhpGenerator;
9:
10: use Nette;
11: use Nette\Utils\Strings;
12:
13:
14: 15: 16: 17: 18: 19:
20: class ClassType
21: {
22: use Nette\SmartObject;
23: use Traits\CommentAware;
24:
25: const TYPE_CLASS = 'class';
26:
27: const TYPE_INTERFACE = 'interface';
28:
29: const TYPE_TRAIT = 'trait';
30:
31:
32: private $namespace;
33:
34:
35: private $name;
36:
37:
38: private $type = 'class';
39:
40:
41: private $final = false;
42:
43:
44: private $abstract = false;
45:
46:
47: private $extends = [];
48:
49:
50: private $implements = [];
51:
52:
53: private $traits = [];
54:
55:
56: private $consts = [];
57:
58:
59: private $properties = [];
60:
61:
62: private $methods = [];
63:
64:
65: 66: 67: 68:
69: public static function from($class)
70: {
71: return (new Factory)->fromClassReflection(
72: $class instanceof \ReflectionClass ? $class : new \ReflectionClass($class)
73: );
74: }
75:
76:
77: 78: 79:
80: public function __construct($name = null, PhpNamespace $namespace = null)
81: {
82: $this->setName($name);
83: $this->namespace = $namespace;
84: }
85:
86:
87: 88: 89:
90: public function __toString()
91: {
92: $traits = [];
93: foreach ($this->traits as $trait => $resolutions) {
94: $traits[] = 'use ' . ($this->namespace ? $this->namespace->unresolveName($trait) : $trait)
95: . ($resolutions ? " {\n\t" . implode(";\n\t", $resolutions) . ";\n}" : ';');
96: }
97:
98: $consts = [];
99: foreach ($this->consts as $const) {
100: $consts[] = Helpers::formatDocComment($const->getComment())
101: . ($const->getVisibility() ? $const->getVisibility() . ' ' : '')
102: . 'const ' . $const->getName() . ' = ' . Helpers::dump($const->getValue()) . ';';
103: }
104:
105: $properties = [];
106: foreach ($this->properties as $property) {
107: $properties[] = Helpers::formatDocComment($property->getComment())
108: . ($property->getVisibility() ?: 'public') . ($property->isStatic() ? ' static' : '') . ' $' . $property->getName()
109: . ($property->value === null ? '' : ' = ' . Helpers::dump($property->value))
110: . ';';
111: }
112:
113: $mapper = function (array $arr) {
114: return $this->namespace ? array_map([$this->namespace, 'unresolveName'], $arr) : $arr;
115: };
116:
117: return Strings::normalize(
118: Helpers::formatDocComment($this->comment . "\n")
119: . ($this->abstract ? 'abstract ' : '')
120: . ($this->final ? 'final ' : '')
121: . ($this->name ? "$this->type $this->name " : '')
122: . ($this->extends ? 'extends ' . implode(', ', $mapper((array) $this->extends)) . ' ' : '')
123: . ($this->implements ? 'implements ' . implode(', ', $mapper($this->implements)) . ' ' : '')
124: . ($this->name ? "\n" : '') . "{\n"
125: . Strings::indent(
126: ($this->traits ? implode("\n", $traits) . "\n\n" : '')
127: . ($this->consts ? implode("\n", $consts) . "\n\n" : '')
128: . ($this->properties ? implode("\n\n", $properties) . "\n\n\n" : '')
129: . ($this->methods ? implode("\n\n\n", $this->methods) . "\n" : ''), 1)
130: . '}'
131: ) . ($this->name ? "\n" : '');
132: }
133:
134:
135: 136: 137:
138: public function getNamespace()
139: {
140: return $this->namespace;
141: }
142:
143:
144: 145: 146: 147:
148: public function setName($name)
149: {
150: if ($name !== null && !Helpers::isIdentifier($name)) {
151: throw new Nette\InvalidArgumentException("Value '$name' is not valid class name.");
152: }
153: $this->name = $name;
154: return $this;
155: }
156:
157:
158: 159: 160:
161: public function getName()
162: {
163: return $this->name;
164: }
165:
166:
167: 168: 169: 170:
171: public function setType($type)
172: {
173: if (!in_array($type, ['class', 'interface', 'trait'], true)) {
174: throw new Nette\InvalidArgumentException('Argument must be class|interface|trait.');
175: }
176: $this->type = $type;
177: return $this;
178: }
179:
180:
181: 182: 183:
184: public function getType()
185: {
186: return $this->type;
187: }
188:
189:
190: 191: 192: 193:
194: public function setFinal($state = true)
195: {
196: $this->final = (bool) $state;
197: return $this;
198: }
199:
200:
201: 202: 203:
204: public function isFinal()
205: {
206: return $this->final;
207: }
208:
209:
210: 211: 212: 213:
214: public function setAbstract($state = true)
215: {
216: $this->abstract = (bool) $state;
217: return $this;
218: }
219:
220:
221: 222: 223:
224: public function isAbstract()
225: {
226: return $this->abstract;
227: }
228:
229:
230: 231: 232: 233:
234: public function setExtends($names)
235: {
236: if (!is_string($names) && !is_array($names)) {
237: throw new Nette\InvalidArgumentException('Argument must be string or string[].');
238: }
239: $this->validate((array) $names);
240: $this->extends = $names;
241: return $this;
242: }
243:
244:
245: 246: 247:
248: public function getExtends()
249: {
250: return $this->extends;
251: }
252:
253:
254: 255: 256: 257:
258: public function addExtend($name)
259: {
260: $this->validate([$name]);
261: $this->extends = (array) $this->extends;
262: $this->extends[] = $name;
263: return $this;
264: }
265:
266:
267: 268: 269: 270:
271: public function setImplements(array $names)
272: {
273: $this->validate($names);
274: $this->implements = $names;
275: return $this;
276: }
277:
278:
279: 280: 281:
282: public function getImplements()
283: {
284: return $this->implements;
285: }
286:
287:
288: 289: 290: 291:
292: public function addImplement($name)
293: {
294: $this->validate([$name]);
295: $this->implements[] = $name;
296: return $this;
297: }
298:
299:
300: 301: 302: 303:
304: public function setTraits(array $names)
305: {
306: $this->validate($names);
307: $this->traits = array_fill_keys($names, []);
308: return $this;
309: }
310:
311:
312: 313: 314:
315: public function getTraits()
316: {
317: return array_keys($this->traits);
318: }
319:
320:
321: 322: 323: 324:
325: public function addTrait($name, array $resolutions = [])
326: {
327: $this->validate([$name]);
328: $this->traits[$name] = $resolutions;
329: return $this;
330: }
331:
332:
333: 334: 335: 336:
337: public function setConsts(array $consts)
338: {
339: return $this->setConstants($consts);
340: }
341:
342:
343: 344: 345: 346:
347: public function getConsts()
348: {
349: return array_map(function ($const) { return $const->getValue(); }, $this->consts);
350: }
351:
352:
353: 354: 355: 356: 357: 358:
359: public function addConst($name, $value)
360: {
361: $this->addConstant($name, $value);
362: return $this;
363: }
364:
365:
366: 367: 368: 369:
370: public function setConstants(array $consts)
371: {
372: $this->consts = [];
373: foreach ($consts as $k => $v) {
374: $const = $v instanceof Constant ? $v : (new Constant($k))->setValue($v);
375: $this->consts[$const->getName()] = $const;
376: }
377: return $this;
378: }
379:
380:
381: 382: 383:
384: public function getConstants()
385: {
386: return $this->consts;
387: }
388:
389:
390: 391: 392: 393: 394:
395: public function addConstant($name, $value)
396: {
397: return $this->consts[$name] = (new Constant($name))->setValue($value);
398: }
399:
400:
401: 402: 403: 404:
405: public function setProperties(array $props)
406: {
407: $this->properties = [];
408: foreach ($props as $v) {
409: if (!$v instanceof Property) {
410: throw new Nette\InvalidArgumentException('Argument must be Nette\PhpGenerator\Property[].');
411: }
412: $this->properties[$v->getName()] = $v;
413: }
414: return $this;
415: }
416:
417:
418: 419: 420:
421: public function getProperties()
422: {
423: return $this->properties;
424: }
425:
426:
427: 428: 429:
430: public function getProperty($name)
431: {
432: if (!isset($this->properties[$name])) {
433: throw new Nette\InvalidArgumentException("Property '$name' not found.");
434: }
435: return $this->properties[$name];
436: }
437:
438:
439: 440: 441: 442: 443:
444: public function addProperty($name, $value = null)
445: {
446: return $this->properties[$name] = (new Property($name))->setValue($value);
447: }
448:
449:
450: 451: 452: 453:
454: public function setMethods(array $methods)
455: {
456: $this->methods = [];
457: foreach ($methods as $v) {
458: if (!$v instanceof Method) {
459: throw new Nette\InvalidArgumentException('Argument must be Nette\PhpGenerator\Method[].');
460: }
461: $this->methods[$v->getName()] = $v->setNamespace($this->namespace);
462: }
463: return $this;
464: }
465:
466:
467: 468: 469:
470: public function getMethods()
471: {
472: return $this->methods;
473: }
474:
475:
476: 477: 478:
479: public function getMethod($name)
480: {
481: if (!isset($this->methods[$name])) {
482: throw new Nette\InvalidArgumentException("Method '$name' not found.");
483: }
484: return $this->methods[$name];
485: }
486:
487:
488: 489: 490: 491:
492: public function addMethod($name)
493: {
494: $method = (new Method($name))->setNamespace($this->namespace);
495: if ($this->type === 'interface') {
496: $method->setBody(false);
497: } else {
498: $method->setVisibility('public');
499: }
500: return $this->methods[$name] = $method;
501: }
502:
503:
504: private function validate(array $names)
505: {
506: foreach ($names as $name) {
507: if (!Helpers::isNamespaceIdentifier($name, true)) {
508: throw new Nette\InvalidArgumentException("Value '$name' is not valid class name.");
509: }
510: }
511: }
512: }
513: