Skip to content

Commit 277f0f9

Browse files
committed
use-statements for functions & constants
1 parent 0e283d9 commit 277f0f9

12 files changed

+151
-48
lines changed

readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,8 @@ You can define use-statements:
582582
$namespace->addUse(Http\Request::class);
583583
// use Http\Request as HttpReq;
584584
$namespace->addUse(Http\Request::class, 'HttpReq');
585+
// use function iter\range;
586+
$namespace->addUseFunction('iter\range');
585587
```
586588

587589
Class Names Resolving

src/PhpGenerator/Extractor.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,13 @@ private function prepareReplacements(array $statements): array
102102
(new NodeFinder)->find($statements, function (Node $node) use (&$replacements, $start) {
103103
if ($node instanceof Node\Name\FullyQualified) {
104104
if ($node->getAttribute('originalName') instanceof Node\Name) {
105-
$type = $node->getAttribute('parent') instanceof Node\Expr\ConstFetch
105+
$of = $node->getAttribute('parent') instanceof Node\Expr\ConstFetch
106106
? PhpNamespace::NAME_CONSTANT
107107
: ($node->getAttribute('parent') instanceof Node\Expr\FuncCall ? PhpNamespace::NAME_FUNCTION : PhpNamespace::NAME_NORMAL);
108108
$replacements[] = [
109109
$node->getStartFilePos() - $start,
110110
$node->getEndFilePos() - $start,
111-
Helpers::tagName($node->toCodeString(), $type),
111+
Helpers::tagName($node->toCodeString(), $of),
112112
];
113113
}
114114

@@ -213,10 +213,13 @@ public function enterNode(Node $node)
213213

214214
private function addUseToNamespace(Node\Stmt\Use_ $node, PhpNamespace $namespace): void
215215
{
216-
if ($node->type === $node::TYPE_NORMAL) {
217-
foreach ($node->uses as $use) {
218-
$namespace->addUse($use->name->toString(), $use->alias ? $use->alias->toString() : null);
219-
}
216+
$of = [
217+
$node::TYPE_NORMAL => PhpNamespace::NAME_NORMAL,
218+
$node::TYPE_FUNCTION => PhpNamespace::NAME_FUNCTION,
219+
$node::TYPE_CONSTANT => PhpNamespace::NAME_CONSTANT,
220+
][$node->type];
221+
foreach ($node->uses as $use) {
222+
$namespace->addUse($use->name->toString(), $use->alias ? $use->alias->toString() : null, $of);
220223
}
221224
}
222225

src/PhpGenerator/Helpers.php

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,25 +83,21 @@ public static function formatDocComment(string $content): string
8383
}
8484

8585

86-
public static function tagName(string $name, string $type = PhpNamespace::NAME_NORMAL): string
86+
public static function tagName(string $name, string $of = PhpNamespace::NAME_NORMAL): string
8787
{
8888
return isset(self::KEYWORDS[strtolower($name)])
8989
? $name
90-
: "/*($type*/$name";
90+
: "/*($of*/$name";
9191
}
9292

9393

9494
public static function simplifyTaggedNames(string $code, ?PhpNamespace $namespace): string
9595
{
9696
return preg_replace_callback('~/\*\(([ncf])\*/([\w\x7f-\xff\\\\]++)~', function ($m) use ($namespace) {
97-
[, $type, $name] = $m;
98-
if (!$namespace) {
99-
return $name;
100-
} elseif ($type === PhpNamespace::NAME_NORMAL) {
101-
return $namespace->simplifyType($name);
102-
} else {
103-
return $namespace->simplifyType(self::extractNamespace($name) . '\\') . self::extractShortName($name);
104-
}
97+
[, $of, $name] = $m;
98+
return $namespace
99+
? $namespace->simplifyType($name, $of)
100+
: $name;
105101
}, $code);
106102
}
107103

src/PhpGenerator/PhpFile.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,9 @@ public function getFunctions(): array
134134

135135

136136
/** @return static */
137-
public function addUse(string $name, string $alias = null): self
137+
public function addUse(string $name, string $alias = null, string $of = PhpNamespace::NAME_NORMAL): self
138138
{
139-
$this->addNamespace('')->addUse($name, $alias);
139+
$this->addNamespace('')->addUse($name, $alias, $of);
140140
return $this;
141141
}
142142

src/PhpGenerator/PhpNamespace.php

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,12 @@ final class PhpNamespace
3737
/** @var bool */
3838
private $bracketedSyntax = false;
3939

40-
/** @var string[] */
41-
private $aliases = [];
40+
/** @var string[][] */
41+
private $aliases = [
42+
self::NAME_NORMAL => [],
43+
self::NAME_FUNCTION => [],
44+
self::NAME_CONSTANT => [],
45+
];
4246

4347
/** @var ClassType[] */
4448
private $classes = [];
@@ -90,44 +94,62 @@ public function getBracketedSyntax(): bool
9094
* @throws InvalidStateException
9195
* @return static
9296
*/
93-
public function addUse(string $name, string $alias = null): self
97+
public function addUse(string $name, string $alias = null, string $of = self::NAME_NORMAL): self
9498
{
9599
if (
96100
!Helpers::isNamespaceIdentifier($name, true)
97101
|| (Helpers::isIdentifier($name) && isset(Helpers::KEYWORDS[strtolower($name)]))
98102
) {
99-
throw new Nette\InvalidArgumentException("Value '$name' is not valid class name.");
103+
throw new Nette\InvalidArgumentException("Value '$name' is not valid class/function/constant name.");
104+
100105
} elseif ($alias && (!Helpers::isIdentifier($alias) || isset(Helpers::KEYWORDS[strtolower($alias)]))) {
101106
throw new Nette\InvalidArgumentException("Value '$alias' is not valid alias.");
102107
}
103108

104109
$name = ltrim($name, '\\');
110+
$aliases = $this->aliases[$of];
111+
$used = [self::NAME_NORMAL => $this->classes, self::NAME_FUNCTION => $this->functions, self::NAME_CONSTANT => []][$of];
112+
105113
if ($alias === null) {
106114
$base = Helpers::extractShortName($name);
107115
$counter = null;
108116
do {
109117
$alias = $base . $counter;
110118
$counter++;
111-
} while ((isset($this->aliases[$alias]) && $this->aliases[$alias] !== $name) || isset($this->classes[$alias]));
119+
} while ((isset($aliases[$alias]) && $aliases[$alias] !== $name) || isset($used[$alias]));
112120

113-
} elseif (isset($this->aliases[$alias]) && $this->aliases[$alias] !== $name) {
121+
} elseif (isset($aliases[$alias]) && $aliases[$alias] !== $name) {
114122
throw new InvalidStateException(
115-
"Alias '$alias' used already for '{$this->aliases[$alias]}', cannot use for '$name'."
123+
"Alias '$alias' used already for '{$aliases[$alias]}', cannot use for '$name'."
116124
);
117-
} elseif (isset($this->classes[$alias])) {
118-
throw new Nette\InvalidStateException("Name '$alias' used already for '$this->name\\{$this->classes[$alias]->getName()}'.");
125+
} elseif (isset($used[$alias])) {
126+
throw new Nette\InvalidStateException("Name '$alias' used already for '$this->name\\{$used[$alias]->getName()}'.");
119127
}
120128

121-
$this->aliases[$alias] = $name;
129+
$this->aliases[$of][$alias] = $name;
122130
return $this;
123131
}
124132

125133

134+
/** @return static */
135+
public function addUseFunction(string $name, string $alias = null): self
136+
{
137+
return $this->addUse($name, $alias, self::NAME_FUNCTION);
138+
}
139+
140+
141+
/** @return static */
142+
public function addUseConstant(string $name, string $alias = null): self
143+
{
144+
return $this->addUse($name, $alias, self::NAME_CONSTANT);
145+
}
146+
147+
126148
/** @return string[] */
127-
public function getUses(): array
149+
public function getUses(string $of = self::NAME_NORMAL): array
128150
{
129-
asort($this->aliases);
130-
return $this->aliases;
151+
asort($this->aliases[$of]);
152+
return $this->aliases[$of];
131153
}
132154

133155

@@ -138,24 +160,34 @@ public function unresolveName(string $name): string
138160
}
139161

140162

141-
public function simplifyType(string $type): string
163+
public function simplifyType(string $type, string $of = self::NAME_NORMAL): string
142164
{
143-
return preg_replace_callback('~[\w\x7f-\xff\\\\]+~', function ($m) { return $this->simplifyName($m[0]); }, $type);
165+
return preg_replace_callback('~[\w\x7f-\xff\\\\]+~', function ($m) use ($of) { return $this->simplifyName($m[0], $of); }, $type);
144166
}
145167

146168

147-
public function simplifyName(string $name): string
169+
public function simplifyName(string $name, string $of = self::NAME_NORMAL): string
148170
{
149171
if (isset(Helpers::KEYWORDS[strtolower($name)]) || $name === '') {
150172
return $name;
151173
}
152174
$name = ltrim($name, '\\');
175+
176+
if ($of !== self::NAME_NORMAL) {
177+
foreach ($this->aliases[$of] as $alias => $original) {
178+
if (strcasecmp($original, $name) === 0) {
179+
return $alias;
180+
}
181+
}
182+
return $this->simplifyName(Helpers::extractNamespace($name) . '\\') . Helpers::extractShortName($name);
183+
}
184+
153185
$lower = strtolower($name);
154186
$res = Strings::startsWith($lower, strtolower($this->name) . '\\')
155187
? substr($name, strlen($this->name) + 1)
156188
: null;
157189

158-
foreach ($this->aliases as $alias => $original) {
190+
foreach ($this->aliases[$of] as $alias => $original) {
159191
if (Strings::startsWith($lower . '\\', strtolower($original) . '\\')) {
160192
$short = $alias . substr($name, strlen($original));
161193
if (!isset($res) || strlen($res) > strlen($short)) {
@@ -164,7 +196,7 @@ public function simplifyName(string $name): string
164196
}
165197
}
166198

167-
return $res ?: ($this->name ? '\\' : '') . $name;
199+
return $res ?? ($this->name ? '\\' : '') . $name;
168200
}
169201

170202

@@ -174,7 +206,7 @@ public function add(ClassType $class): self
174206
$name = $class->getName();
175207
if ($name === null) {
176208
throw new Nette\InvalidArgumentException('Class does not have a name.');
177-
} elseif ($orig = $this->aliases[$name] ?? null) {
209+
} elseif ($orig = $this->aliases[self::NAME_NORMAL][$name] ?? null) {
178210
throw new Nette\InvalidStateException("Name '$name' used already as alias for $orig.");
179211
}
180212
$this->classes[$name] = $class;
@@ -209,6 +241,9 @@ public function addEnum(string $name): ClassType
209241

210242
public function addFunction(string $name): GlobalFunction
211243
{
244+
if ($orig = $this->aliases[self::NAME_FUNCTION][$name] ?? null) {
245+
throw new Nette\InvalidStateException("Name '$name' used already as alias for $orig.");
246+
}
212247
return $this->functions[$name] = new GlobalFunction($name);
213248
}
214249

src/PhpGenerator/Printer.php

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,9 @@ public function printNamespace(PhpNamespace $namespace): string
234234
{
235235
$this->namespace = $this->resolveTypes ? $namespace : null;
236236
$name = $namespace->getName();
237-
$uses = $this->printUses($namespace);
237+
$uses = $this->printUses($namespace)
238+
. $this->printUses($namespace, PhpNamespace::NAME_FUNCTION)
239+
. $this->printUses($namespace, PhpNamespace::NAME_CONSTANT);
238240

239241
$items = [];
240242
foreach ($namespace->getClasses() as $class) {
@@ -244,7 +246,7 @@ public function printNamespace(PhpNamespace $namespace): string
244246
$items[] = $this->printFunction($function, $namespace);
245247
}
246248

247-
$body = ($uses ? $uses . "\n\n" : '')
249+
$body = ($uses ? $uses . "\n" : '')
248250
. implode("\n", $items);
249251

250252
if ($namespace->hasBracketedSyntax()) {
@@ -276,16 +278,21 @@ public function printFile(PhpFile $file): string
276278
}
277279

278280

279-
protected function printUses(PhpNamespace $namespace): string
281+
protected function printUses(PhpNamespace $namespace, string $of = PhpNamespace::NAME_NORMAL): string
280282
{
283+
$prefix = [
284+
PhpNamespace::NAME_NORMAL => '',
285+
PhpNamespace::NAME_FUNCTION => 'function ',
286+
PhpNamespace::NAME_CONSTANT => 'const ',
287+
][$of];
281288
$name = $namespace->getName();
282289
$uses = [];
283-
foreach ($namespace->getUses() as $alias => $original) {
290+
foreach ($namespace->getUses($of) as $alias => $original) {
284291
$uses[] = Helpers::extractShortName($original) === $alias
285-
? "use $original;"
286-
: "use $original as $alias;";
292+
? "use $prefix$original;\n"
293+
: "use $prefix$original as $alias;\n";
287294
}
288-
return implode("\n", $uses);
295+
return implode('', $uses);
289296
}
290297

291298

tests/PhpGenerator/PhpNamespace.aliases.phpt

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,36 @@ Assert::same('foo\A', $namespace->simplifyName('foo\A'));
1818
$namespace->addUse('Bar\C');
1919

2020
Assert::same('Bar', $namespace->simplifyName('Bar'));
21-
Assert::same('C', $namespace->simplifyName('bar\C'));
21+
Assert::same('C', $namespace->simplifyName('Bar\C'));
2222
Assert::same('C\D', $namespace->simplifyName('Bar\C\D'));
2323

2424
foreach (['String', 'string', 'int', 'float', 'bool', 'array', 'callable', 'self', 'parent', ''] as $type) {
2525
Assert::same($type, $namespace->simplifyName($type));
2626
}
2727

28+
$namespace->addUseFunction('Foo\a');
29+
30+
Assert::same('Bar\c', $namespace->simplifyName('Bar\c', $namespace::NAME_FUNCTION));
31+
Assert::same('a', $namespace->simplifyName('Foo\A', $namespace::NAME_FUNCTION));
32+
Assert::same('Foo\a\b', $namespace->simplifyName('Foo\a\b', $namespace::NAME_FUNCTION));
33+
34+
foreach (['String', 'string', 'int', 'float', 'bool', 'array', 'callable', 'self', 'parent', ''] as $type) {
35+
Assert::same($type, $namespace->simplifyName($type, $namespace::NAME_FUNCTION));
36+
}
37+
38+
$namespace->addUseFunction('Bar\c');
39+
40+
Assert::same('Bar', $namespace->simplifyName('Bar', $namespace::NAME_FUNCTION));
41+
Assert::same('c', $namespace->simplifyName('Bar\c', $namespace::NAME_FUNCTION));
42+
Assert::same('C\d', $namespace->simplifyName('Bar\C\d', $namespace::NAME_FUNCTION));
43+
44+
$namespace->addUseConstant('Bar\c');
45+
46+
Assert::same('Bar', $namespace->simplifyName('Bar', $namespace::NAME_CONSTANT));
47+
Assert::same('c', $namespace->simplifyName('Bar\c', $namespace::NAME_CONSTANT));
48+
Assert::same('C\d', $namespace->simplifyName('Bar\C\d', $namespace::NAME_CONSTANT));
49+
50+
2851

2952
// namespace
3053
$namespace = new PhpNamespace('Foo');
@@ -53,6 +76,19 @@ Assert::same('C\D', $namespace->simplifyName('Bar\C\D'));
5376
Assert::same('A<C, C\D>', $namespace->simplifyType('foo\A<\bar\C, Bar\C\D>'));
5477
Assert::same('žluťoučký', $namespace->simplifyType('foo\žluťoučký'));
5578

79+
$namespace->addUseFunction('Foo\a');
80+
81+
Assert::same('\Bar\c', $namespace->simplifyName('Bar\c', $namespace::NAME_FUNCTION));
82+
Assert::same('a', $namespace->simplifyName('Foo\a', $namespace::NAME_FUNCTION));
83+
Assert::same('C\b', $namespace->simplifyName('Foo\C\b', $namespace::NAME_FUNCTION));
84+
Assert::same('a\b', $namespace->simplifyName('Foo\a\b', $namespace::NAME_FUNCTION));
85+
86+
$namespace->addUseFunction('Bar\c');
87+
88+
Assert::same('\Bar', $namespace->simplifyName('Bar', $namespace::NAME_FUNCTION));
89+
Assert::same('c', $namespace->simplifyName('Bar\c', $namespace::NAME_FUNCTION));
90+
Assert::same('C\d', $namespace->simplifyName('Bar\c\d', $namespace::NAME_FUNCTION));
91+
5692

5793
// duplicity
5894
$namespace = new PhpNamespace('Foo');
@@ -67,7 +103,18 @@ Assert::exception(function () use ($namespace) {
67103
$namespace->addUse('Lorem\B', 'B');
68104
}, Nette\InvalidStateException::class, "Name 'B' used already for 'Foo\\B'.");
69105

70-
Assert::same(['C' => 'Bar\\C'], $namespace->getUses());
106+
$namespace->addUseFunction('Bar\f1');
107+
Assert::exception(function () use ($namespace) {
108+
$namespace->addFunction('f1');
109+
}, Nette\InvalidStateException::class, "Name 'f1' used already as alias for Bar\\f1.");
110+
111+
$namespace->addFunction('f2');
112+
Assert::exception(function () use ($namespace) {
113+
$namespace->addUseFunction('Bar\f2', 'f2');
114+
}, Nette\InvalidStateException::class, "Name 'f2' used already for 'Foo\\f2'.");
115+
116+
Assert::same(['C' => 'Bar\C'], $namespace->getUses());
117+
Assert::same(['f1' => 'Bar\f1'], $namespace->getUses($namespace::NAME_FUNCTION));
71118

72119

73120
// alias generation
@@ -101,3 +148,8 @@ $namespace = new PhpNamespace('Foo');
101148
$namespace->addUse('Bar\C');
102149
$namespace->addUse('C');
103150
Assert::same('C1', $namespace->simplifyName('C'));
151+
152+
$namespace = new PhpNamespace('Foo');
153+
$namespace->addUseFunction('Bar\c');
154+
$namespace->addUseFunction('c');
155+
Assert::same('c1', $namespace->simplifyName('c', $namespace::NAME_FUNCTION));

tests/PhpGenerator/PhpNamespace.print.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ $namespace = new PhpNamespace('Foo');
1111

1212
$namespace->addUse('Foo');
1313
$namespace->addUse('Bar\C');
14+
$namespace->addUseFunction('Bar\c');
15+
$namespace->addUseConstant('Bar\FOO');
1416

1517
$classA = $namespace->addClass('A');
1618
$interfaceB = $namespace->addInterface('B');

tests/PhpGenerator/expected/Factory.fromCode.bodies.expect

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ declare(strict_types=1);
55
namespace Abc;
66

77
use Nette;
8+
use function substr;
9+
use const BAR;
810

911
abstract class Class7
1012
{

0 commit comments

Comments
 (0)