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