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