Skip to content

Commit 6334ba8

Browse files
committed
Add class \SetBased\Helper\CodeStore\Importing.
1 parent 53f23ec commit 6334ba8

File tree

2 files changed

+444
-0
lines changed

2 files changed

+444
-0
lines changed

src/Importing.php

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace SetBased\Helper\CodeStore;
5+
6+
/**
7+
* Helper class for generating statements for importing classes.
8+
*/
9+
class Importing
10+
{
11+
//--------------------------------------------------------------------------------------------------------------------
12+
/**
13+
* The fully qualified names to import.
14+
*
15+
* @var array
16+
*/
17+
private $classes = [];
18+
19+
/**
20+
* The import statements.
21+
*
22+
* @var string[]
23+
*/
24+
private $imports;
25+
26+
/**
27+
* The namespace.
28+
*
29+
* @var string
30+
*/
31+
private $namespace;
32+
33+
/**
34+
* The replace pairs from fully qualified name to imported name.
35+
*
36+
* @var array
37+
*/
38+
private $replace;
39+
40+
//--------------------------------------------------------------------------------------------------------------------
41+
/**
42+
* Object constructor.
43+
*
44+
* @param string $namespace The namespace.
45+
*/
46+
public function __construct(string $namespace)
47+
{
48+
$this->namespace = self::fullyQualify($namespace);
49+
}
50+
51+
//--------------------------------------------------------------------------------------------------------------------
52+
/**
53+
* The (fully) qualified name optionally without leading slash.
54+
*
55+
* @param string $name The fully qualified name with lead slash.
56+
*
57+
* @return string
58+
*/
59+
public static function fullyQualify(string $name): string
60+
{
61+
return '\\'.ltrim($name, '\\');
62+
}
63+
64+
//--------------------------------------------------------------------------------------------------------------------
65+
/**
66+
* Test for name collision.
67+
*
68+
* @param array $rawImports The (raw) uses data.
69+
* @param string $fullyQualifiedName The fully qualified class name.
70+
*
71+
* @return bool
72+
*/
73+
private static function collision1(array $rawImports, string $fullyQualifiedName): bool
74+
{
75+
[, $name] = self::split($fullyQualifiedName);
76+
77+
foreach ($rawImports as $rawImport)
78+
{
79+
if ($rawImport['fully_qualified_name']!==$fullyQualifiedName && $rawImport['name']===$name)
80+
{
81+
return true;
82+
}
83+
}
84+
85+
return false;
86+
}
87+
88+
//--------------------------------------------------------------------------------------------------------------------
89+
/**
90+
* Test for collision on alias or name.
91+
*
92+
* @param array $rawImports The (raw) uses data.
93+
* @param string $fullyQualifiedName The fully qualified class name.
94+
* @param string $alias The alias.
95+
*
96+
* @return bool
97+
*/
98+
private static function collision2(array $rawImports, string $fullyQualifiedName, string $alias): bool
99+
{
100+
foreach ($rawImports as $rawImport)
101+
{
102+
if ($rawImport['fully_qualified_name']!==$fullyQualifiedName &&
103+
($rawImport['name']===$alias || $rawImport['alias']===$alias))
104+
{
105+
return true;
106+
}
107+
}
108+
109+
return false;
110+
}
111+
112+
//--------------------------------------------------------------------------------------------------------------------
113+
/**
114+
* Splits a fully qualified name into a namespace and class name.
115+
*
116+
* @param string $name The fully qualified name.
117+
*
118+
* @return array
119+
*/
120+
private static function split(string $name): array
121+
{
122+
$parts = explode('\\', self::fullyQualify($name));
123+
$name = array_pop($parts);
124+
125+
return [implode('\\', $parts), $name];
126+
}
127+
128+
//--------------------------------------------------------------------------------------------------------------------
129+
/**
130+
* Add a class to import.
131+
*
132+
* @param string $class The fully qualified name of the class.
133+
*/
134+
public function addClass(string $class): void
135+
{
136+
$this->classes[] = self::fullyQualify($class);
137+
}
138+
139+
//--------------------------------------------------------------------------------------------------------------------
140+
/**
141+
* Returns the import statements.
142+
*
143+
* @return string[]
144+
*/
145+
public function imports(): array
146+
{
147+
return $this->imports;
148+
}
149+
150+
//--------------------------------------------------------------------------------------------------------------------
151+
/**
152+
* Prepares the data for retrieving import statements and replace pairs (see methods replacePairs() and imports().
153+
*/
154+
public function prepare(): void
155+
{
156+
$rawUses = $this->prepare0();
157+
$this->prepare1($rawUses);
158+
$this->prepare2($rawUses);
159+
}
160+
161+
//--------------------------------------------------------------------------------------------------------------------
162+
/**
163+
* Returns the replace pairs from fully qualified name to imported name.
164+
*
165+
* @return array
166+
*/
167+
public function replacePairs(): array
168+
{
169+
return $this->replace;
170+
}
171+
172+
//--------------------------------------------------------------------------------------------------------------------
173+
/**
174+
* Returns the imported name given a fully qualified name.
175+
*
176+
* @param string $fullyQualifiedName The fully qualified name.
177+
*
178+
* @return string
179+
*/
180+
public function simplyFullyQualifiedName(string $fullyQualifiedName): string
181+
{
182+
return $this->replace[self::fullyQualify($fullyQualifiedName)];
183+
}
184+
185+
//--------------------------------------------------------------------------------------------------------------------
186+
/**
187+
* Returns raw data about classes to import.
188+
*
189+
* @return array
190+
*/
191+
private function prepare0(): array
192+
{
193+
$this->classes = array_unique($this->classes);
194+
sort($this->classes);
195+
196+
$rawImports = [];
197+
foreach ($this->classes as $fullyQualifiedName)
198+
{
199+
[$namespace, $name] = self::split($fullyQualifiedName);
200+
201+
$rawImports[] = ['fully_qualified_name' => $fullyQualifiedName,
202+
'namespace' => $namespace,
203+
'name' => $name,
204+
'alias' => null,
205+
'import' => ($namespace!==$this->namespace)];
206+
}
207+
208+
foreach ($rawImports as &$rawImport)
209+
{
210+
if (self::collision1($rawImports, $rawImport['fully_qualified_name']))
211+
{
212+
$i = 1;
213+
do
214+
{
215+
$alias = sprintf('%s%s%d', $rawImport['name'], 'Alias', $i);
216+
$i++;
217+
} while (self::collision2($rawImports, $this->namespace, $alias));
218+
219+
$rawImport['alias'] = $alias;
220+
}
221+
}
222+
223+
return $rawImports;
224+
}
225+
226+
//--------------------------------------------------------------------------------------------------------------------
227+
/**
228+
* Prepares the import statements.
229+
*
230+
* @param array $rawImports The raw data about classes to import.
231+
*/
232+
private function prepare1(array $rawImports): void
233+
{
234+
$this->imports = [];
235+
foreach ($rawImports as $rawImport)
236+
{
237+
if ($rawImport['import'])
238+
{
239+
if ($rawImport['alias']===null)
240+
{
241+
$this->imports[] = sprintf('use %s;', ltrim($rawImport['fully_qualified_name'], '\\'));
242+
}
243+
else
244+
{
245+
$this->imports[] = sprintf('use %s as %s;', ltrim($rawImport['fully_qualified_name'], '\\'), $rawImport['alias']);
246+
}
247+
}
248+
}
249+
250+
usort($this->imports, function (string $a, string $b) {
251+
return str_replace('\\', ' ', $a)<=>str_replace('\\', ' ', $b);
252+
});
253+
}
254+
255+
//--------------------------------------------------------------------------------------------------------------------
256+
/**
257+
* Prepares the replace pairs.
258+
*
259+
* @param array $rawImports The raw data about classes to import.
260+
*/
261+
private function prepare2(array $rawImports): void
262+
{
263+
usort($rawImports, function (array $a, array $b) {
264+
$cmp = (strlen($b['fully_qualified_name'])<=>strlen($a['fully_qualified_name']));
265+
if ($cmp==0)
266+
{
267+
$cmp = ($a['fully_qualified_name']<=>$b['fully_qualified_name']);
268+
}
269+
270+
return $cmp;
271+
});
272+
273+
$this->replace = [];
274+
foreach ($rawImports as $rawImport)
275+
{
276+
$this->replace[$rawImport['fully_qualified_name']] = $rawImport['alias'] ?? $rawImport['name'];
277+
}
278+
}
279+
280+
//--------------------------------------------------------------------------------------------------------------------
281+
}
282+
283+
//----------------------------------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)