Passed
Branch master (b33026)
by Divine Niiquaye
159:36 queued 107:08
created

FacadeProxy::resolveProxies()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 7

Importance

Changes 0
Metric Value
eloc 14
c 0
b 0
f 0
dl 0
loc 27
ccs 15
cts 15
cp 1
rs 8.8333
cc 7
nc 6
nop 3
crap 7
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
    private array $proxies = [];
43
44 2
    public function __construct(ContainerInterface $container)
45
    {
46 2
        $this->container = $container;
47 2
    }
48
49
    /**
50
     * Register(s) service{s) found in container as shared proxy facade(s).
51
     */
52 2
    public function proxy(string ...$services): void
53
    {
54 2
        foreach ($services as $service) {
55 2
            $id = \str_replace(['.', '_', '\\'], '', \lcfirst(\ucwords($service, '._')));
56
57 2
            if (!$this->container instanceof ContainerBuilder) {
58 1
                Facade::$proxies[$id] = $service;
59
60 1
                continue;
61
            }
62
63 1
            $this->proxies[$id] = $service;
64
        }
65 2
    }
66
67
    /**
68
     * This build method works with container builder.
69
     *
70
     * @param string $className for compiled facade class
71
     */
72 2
    public function build(string $className = 'Facade'): ?string
73
    {
74
        /** @var ContainerBuilder */
75 2
        $container = $this->container;
76
77 2
        if ([] !== $proxiedServices = $this->proxies) {
78 1
            $astNodes = [];
79 1
            $builder = $container->getBuilder();
0 ignored issues
show
Bug introduced by
The method getBuilder() does not exist on Psr\Container\ContainerInterface. It seems like you code against a sub-type of Psr\Container\ContainerInterface such as Rade\DI\AbstractContainer. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

79
            /** @scrutinizer ignore-call */ 
80
            $builder = $container->getBuilder();
Loading history...
80 1
            \ksort($proxiedServices);
81
82 1
            $astNodes[] = new Declare_([new DeclareDeclare('strict_types', $builder->val(1))]);
83 1
            $classNode = $builder->class($className)->extend('\Rade\DI\Facade\Facade')->setDocComment(CodePrinter::COMMENT);
84
85 1
            $classNode->addStmts($this->resolveProxies($container, $builder, $proxiedServices));
86 1
            $astNodes[] = $classNode->getNode();
87
88 1
            return CodePrinter::print($astNodes);
89
        }
90
91 1
        return null;
92
    }
93
94
    /**
95
     * This method resolves the proxies from container builder.
96
     *
97
     * @param string[] $proxiedServices
98
     *
99
     * @return \PhpParser\Builder\Method[]
100
     */
101 1
    protected function resolveProxies(ContainerBuilder $container, BuilderFactory $builder, array $proxiedServices)
102
    {
103 1
        $builtProxies = [];
104
105 1
        foreach ($proxiedServices as $method => $proxy) {
106 1
            if (!$container->has($proxy)) {
107 1
                continue;
108
            }
109
110 1
            $definition = $container->service($proxy);
111 1
            $proxyNode = $builder->method($method)->makePublic()->makeStatic();
112
113 1
            if ($definition instanceof Definition) {
114 1
                if (!$definition->isPublic()) {
115 1
                    continue;
116
                }
117
118 1
                if (!empty($type = $definition->getType())) {
119 1
                    $proxyNode->setReturnType(\is_array($type) ? new UnionType(\array_map(fn ($type) => new Name($type), $type)) : $type);
0 ignored issues
show
Bug introduced by
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 ignore-type  annotation

119
                    $proxyNode->setReturnType(/** @scrutinizer ignore-type */ \is_array($type) ? new UnionType(\array_map(fn ($type) => new Name($type), $type)) : $type);
Loading history...
120
                }
121
            }
122
123 1
            $body = $builder->methodCall(new StaticPropertyFetch(new Name('self'), 'container'), 'get', [$proxy]);
124 1
            $builtProxies[] = $proxyNode->addStmt(new Return_($body));
125
        }
126
127 1
        return $builtProxies;
128
    }
129
}
130