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