Issues (16)

src/Facade/FacadeProxy.php (2 issues)

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
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

80
            /** @scrutinizer ignore-call */ 
81
            $builder = $container->getBuilder();
Loading history...
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 ignore-type  annotation

120
                    $proxyNode->setReturnType(/** @scrutinizer ignore-type */ \is_array($type) ? new UnionType(\array_map(fn ($type) => new Name($type), $type)) : $type);
Loading history...
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