1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | /* |
||||
6 | * This file is part of DivineNii opensource projects. |
||||
7 | * |
||||
8 | * PHP version 7.4 and above required |
||||
9 | * |
||||
10 | * @author Divine Niiquaye Ibok <[email protected]> |
||||
11 | * @copyright 2021 DivineNii (https://divinenii.com/) |
||||
12 | * @license https://opensource.org/licenses/BSD-3-Clause License |
||||
13 | * |
||||
14 | * For the full copyright and license information, please view the LICENSE |
||||
15 | * file that was distributed with this source code. |
||||
16 | */ |
||||
17 | |||||
18 | namespace Rade\DI\Facade; |
||||
19 | |||||
20 | use PhpParser\BuilderFactory; |
||||
21 | use PhpParser\Node\{ |
||||
22 | Expr\StaticPropertyFetch, |
||||
23 | Name, |
||||
24 | Stmt\Declare_, |
||||
25 | Stmt\DeclareDeclare, |
||||
26 | Stmt\Return_, |
||||
27 | UnionType |
||||
28 | }; |
||||
29 | use Psr\Container\ContainerInterface; |
||||
30 | use Rade\DI\Builder\CodePrinter; |
||||
31 | use Rade\DI\{ContainerBuilder, Definition}; |
||||
32 | |||||
33 | /** |
||||
34 | * A Proxy manager for implementing laravel like facade system. |
||||
35 | * |
||||
36 | * @author Divine Niiquaye Ibok <[email protected]> |
||||
37 | */ |
||||
38 | class FacadeProxy |
||||
39 | { |
||||
40 | private ContainerInterface $container; |
||||
41 | |||||
42 | /** @var array<string,string> */ |
||||
43 | private array $proxies = []; |
||||
44 | |||||
45 | 2 | public function __construct(ContainerInterface $container) |
|||
46 | { |
||||
47 | 2 | $this->container = $container; |
|||
48 | 2 | } |
|||
49 | |||||
50 | /** |
||||
51 | * Register(s) service{s) found in container as shared proxy facade(s). |
||||
52 | */ |
||||
53 | 2 | public function proxy(string ...$services): void |
|||
54 | { |
||||
55 | 2 | foreach ($services as $service) { |
|||
56 | 2 | $id = \str_replace(['.', '_', '\\'], '', \lcfirst(\ucwords($service, '._'))); |
|||
57 | |||||
58 | 2 | if (!$this->container instanceof ContainerBuilder) { |
|||
59 | 1 | Facade::$proxies[$id] = $service; |
|||
60 | |||||
61 | 1 | continue; |
|||
62 | } |
||||
63 | |||||
64 | 1 | $this->proxies[$id] = $service; |
|||
65 | } |
||||
66 | 2 | } |
|||
67 | |||||
68 | /** |
||||
69 | * This build method works with container builder. |
||||
70 | * |
||||
71 | * @param string $className for compiled facade class |
||||
72 | */ |
||||
73 | 2 | public function build(string $className = 'Facade'): ?string |
|||
74 | { |
||||
75 | /** @var ContainerBuilder */ |
||||
76 | 2 | $container = $this->container; |
|||
77 | |||||
78 | 2 | if ([] !== $proxiedServices = $this->proxies) { |
|||
79 | 1 | $astNodes = []; |
|||
80 | 1 | $builder = $container->getBuilder(); |
|||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
81 | 1 | \ksort($proxiedServices); |
|||
82 | |||||
83 | 1 | $astNodes[] = new Declare_([new DeclareDeclare('strict_types', $builder->val(1))]); |
|||
84 | 1 | $classNode = $builder->class($className)->extend('\Rade\DI\Facade\Facade')->setDocComment(CodePrinter::COMMENT); |
|||
85 | |||||
86 | 1 | $classNode->addStmts($this->resolveProxies($container, $builder, $proxiedServices)); |
|||
87 | 1 | $astNodes[] = $classNode->getNode(); |
|||
88 | |||||
89 | 1 | return CodePrinter::print($astNodes); |
|||
90 | } |
||||
91 | |||||
92 | 1 | return null; |
|||
93 | } |
||||
94 | |||||
95 | /** |
||||
96 | * This method resolves the proxies from container builder. |
||||
97 | * |
||||
98 | * @param string[] $proxiedServices |
||||
99 | * |
||||
100 | * @return \PhpParser\Builder\Method[] |
||||
101 | */ |
||||
102 | 1 | protected function resolveProxies(ContainerBuilder $container, BuilderFactory $builder, array $proxiedServices): array |
|||
103 | { |
||||
104 | 1 | $builtProxies = []; |
|||
105 | |||||
106 | 1 | foreach ($proxiedServices as $method => $proxy) { |
|||
107 | 1 | if (!$container->has($proxy)) { |
|||
108 | 1 | continue; |
|||
109 | } |
||||
110 | |||||
111 | 1 | $definition = $container->service($proxy); |
|||
112 | 1 | $proxyNode = $builder->method($method)->makePublic()->makeStatic(); |
|||
113 | |||||
114 | 1 | if ($definition instanceof Definition) { |
|||
115 | 1 | if (!$definition->isPublic()) { |
|||
116 | 1 | continue; |
|||
117 | } |
||||
118 | |||||
119 | 1 | if (!empty($type = $definition->getType())) { |
|||
120 | 1 | $proxyNode->setReturnType(\is_array($type) ? new UnionType(\array_map(fn ($type) => new Name($type), $type)) : $type); |
|||
0 ignored issues
–
show
It seems like
is_array($type) ? new Ph.... */ }, $type)) : $type can also be of type PhpParser\Node\UnionType ; however, parameter $type of PhpParser\Builder\FunctionLike::setReturnType() does only seem to accept PhpParser\Node\Name|PhpP...ode\NullableType|string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
121 | } |
||||
122 | } |
||||
123 | |||||
124 | 1 | $body = $builder->methodCall(new StaticPropertyFetch(new Name('self'), 'container'), 'get', [$proxy]); |
|||
125 | 1 | $builtProxies[] = $proxyNode->addStmt(new Return_($body)); |
|||
126 | } |
||||
127 | |||||
128 | 1 | return $builtProxies; |
|||
129 | } |
||||
130 | } |
||||
131 |