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