1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\PhpGenerator;
9:
10: use Nette;
11: use Nette\InvalidStateException;
12: use Nette\Utils\Strings;
13:
14:
15: 16: 17: 18: 19: 20: 21: 22:
23: class PhpNamespace
24: {
25: use Nette\SmartObject;
26:
27: private static $keywords = [
28: 'string' => 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1,
29: 'callable' => 1, 'iterable' => 1, 'void' => 1, 'self' => 1, 'parent' => 1,
30: ];
31:
32:
33: private $name;
34:
35:
36: private $bracketedSyntax = false;
37:
38:
39: private $uses = [];
40:
41:
42: private $classes = [];
43:
44:
45: 46: 47:
48: public function __construct($name = null)
49: {
50: if ($name && !Helpers::isNamespaceIdentifier($name)) {
51: throw new Nette\InvalidArgumentException("Value '$name' is not valid name.");
52: }
53: $this->name = (string) $name;
54: }
55:
56:
57:
58: public function setName($name)
59: {
60: $this->__construct($name);
61: return $this;
62: }
63:
64:
65: 66: 67:
68: public function getName()
69: {
70: return $this->name ?: null;
71: }
72:
73:
74: 75: 76: 77: 78:
79: public function setBracketedSyntax($state = true)
80: {
81: $this->bracketedSyntax = (bool) $state;
82: return $this;
83: }
84:
85:
86: 87: 88:
89: public function getBracketedSyntax()
90: {
91: return $this->bracketedSyntax;
92: }
93:
94:
95: 96: 97: 98: 99: 100: 101:
102: public function addUse($name, $alias = null, &$aliasOut = null)
103: {
104: $name = ltrim($name, '\\');
105: if ($alias === null && $this->name === Helpers::extractNamespace($name)) {
106: $alias = Helpers::extractShortName($name);
107: }
108: if ($alias === null) {
109: $path = explode('\\', $name);
110: $counter = null;
111: do {
112: if (empty($path)) {
113: $counter++;
114: } else {
115: $alias = array_pop($path) . $alias;
116: }
117: } while (isset($this->uses[$alias . $counter]) && $this->uses[$alias . $counter] !== $name);
118: $alias .= $counter;
119:
120: } elseif (isset($this->uses[$alias]) && $this->uses[$alias] !== $name) {
121: throw new InvalidStateException(
122: "Alias '$alias' used already for '{$this->uses[$alias]}', cannot use for '{$name}'."
123: );
124: }
125:
126: $aliasOut = $alias;
127: $this->uses[$alias] = $name;
128: return $this;
129: }
130:
131:
132: 133: 134:
135: public function getUses()
136: {
137: return $this->uses;
138: }
139:
140:
141: 142: 143: 144:
145: public function unresolveName($name)
146: {
147: if (isset(self::$keywords[strtolower($name)]) || $name === '') {
148: return $name;
149: }
150: $name = ltrim($name, '\\');
151: $res = null;
152: $lower = strtolower($name);
153: foreach ($this->uses as $alias => $for) {
154: if (Strings::startsWith($lower . '\\', strtolower($for) . '\\')) {
155: $short = $alias . substr($name, strlen($for));
156: if (!isset($res) || strlen($res) > strlen($short)) {
157: $res = $short;
158: }
159: }
160: }
161:
162: if (!$res && Strings::startsWith($lower, strtolower($this->name) . '\\')) {
163: return substr($name, strlen($this->name) + 1);
164: } else {
165: return $res ?: ($this->name ? '\\' : '') . $name;
166: }
167: }
168:
169:
170: 171: 172: 173:
174: public function addClass($name)
175: {
176: if (!isset($this->classes[$name])) {
177: $this->addUse($this->name . '\\' . $name);
178: $this->classes[$name] = new ClassType($name, $this);
179: }
180: return $this->classes[$name];
181: }
182:
183:
184: 185: 186: 187:
188: public function addInterface($name)
189: {
190: return $this->addClass($name)->setType(ClassType::TYPE_INTERFACE);
191: }
192:
193:
194: 195: 196: 197:
198: public function addTrait($name)
199: {
200: return $this->addClass($name)->setType(ClassType::TYPE_TRAIT);
201: }
202:
203:
204: 205: 206:
207: public function getClasses()
208: {
209: return $this->classes;
210: }
211:
212:
213: 214: 215:
216: public function __toString()
217: {
218: $uses = [];
219: asort($this->uses);
220: foreach ($this->uses as $alias => $name) {
221: $useNamespace = Helpers::extractNamespace($name);
222:
223: if ($this->name !== $useNamespace) {
224: if ($alias === $name || substr($name, -(strlen($alias) + 1)) === '\\' . $alias) {
225: $uses[] = "use {$name};";
226: } else {
227: $uses[] = "use {$name} as {$alias};";
228: }
229: }
230: }
231:
232: $body = ($uses ? implode("\n", $uses) . "\n\n" : '')
233: . implode("\n", $this->classes);
234:
235: if ($this->bracketedSyntax) {
236: return 'namespace' . ($this->name ? ' ' . $this->name : '') . " {\n\n"
237: . Strings::indent($body)
238: . "\n}\n";
239:
240: } else {
241: return ($this->name ? "namespace {$this->name};\n\n" : '')
242: . $body;
243: }
244: }
245: }
246: