Test Failed
Pull Request — master (#37)
by Divine Niiquaye
02:47
created

DefinitionsSplitter   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 116
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 19
eloc 55
c 1
b 0
f 0
dl 0
loc 116
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A traitHash() 0 3 1
B enterNode() 0 33 7
A getTraits() 0 3 1
B buildTraits() 0 43 9
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\NodeVisitor;
19
20
use PhpParser\Node\Expr\Include_;
21
use PhpParser\Node\Scalar\String_;
22
use PhpParser\Node\Stmt\{Class_, Declare_, Expression};
23
use PhpParser\NodeVisitorAbstract;
24
use Rade\DI\Builder\CodePrinter;
25
use Symfony\Component\Config\ConfigCache;
26
27
/**
28
 * This class splits definitions equally with the total amount of $maxDefinitions
29
 * into traits, and imported as use traits into compiled container class.
30
 *
31
 * @author Divine Niiquaye Ibok <[email protected]>
32
 */
33
class DefinitionsSplitter extends NodeVisitorAbstract
34
{
35
    private int $maxCount;
36
37
    private string $fileName;
38
39
    private array $traits = [];
40
41
    private ?string $previousTrait = null;
42
43
    private \PhpParser\BuilderFactory $builder;
44
45
    private ?Declare_ $strictDeclare = null;
46
47
    public function __construct(int $maxDefinitions = 500, string $fileName = 'definitions_autoload.php')
48
    {
49
        $this->maxCount = $maxDefinitions;
50
        $this->fileName = $fileName;
51
        $this->builder = new \PhpParser\BuilderFactory();
52
    }
53
54
    public function getTraits(): array
55
    {
56
        return $this->traits;
57
    }
58
59
    /**
60
     * Use this function to build generated traits into a cache directory
61
     * and return the file that requires all traits.
62
     */
63
    public function buildTraits(string $cacheDirectory, bool $debug = false): string
64
    {
65
        $traitsDirectory = \rtrim($cacheDirectory, '/') . '/Definitions' . $this->traitHash($cacheDirectory);
66
        $autoLoadAst = [];
67
68
        if (null !== $this->strictDeclare) {
69
            $autoLoadAst[] = $this->strictDeclare;
70
        }
71
72
        foreach ($this->traits as $traitName => $traitStmts) {
73
            $cache = new ConfigCache($traitsDirectory . '/' . $traitName . '.php', $debug);
74
            $autoLoadInclude = new Expression(new Include_(new String_($cache->getPath()), Include_::TYPE_REQUIRE));
75
76
            if (\count($autoLoadAst) <= 1) {
77
                $autoLoadInclude->setDocComment(new \PhpParser\Comment\Doc(\str_replace('class', 'file', CodePrinter::COMMENT)));
78
            }
79
80
            if (!$cache->isFresh() || $debug) {
81
                $traitAst = [];
82
                $traitComment = "\n *\n" . ' * @property array<int,mixed> $services' . \PHP_EOL . ' * @property array<int,mixed> $privates';
83
84
                if (null !== $this->strictDeclare) {
85
                    $traitAst[] = $this->strictDeclare;
86
                }
87
88
                $traitAst[] = $this->builder->trait($traitName)
89
                    ->setDocComment(\strtr(CodePrinter::COMMENT, ['class' => 'trait', '.' => '.' . $traitComment . \PHP_EOL]))
90
                    ->addStmts($traitStmts)
91
                    ->getNode();
92
93
                $cache->write(CodePrinter::print($traitAst));
94
            }
95
96
            $autoLoadAst[] = $autoLoadInclude;
97
        }
98
99
        $autoloadCache = new ConfigCache(\rtrim($cacheDirectory, '/') . '/' . $this->fileName, $debug);
100
101
        if (!$autoloadCache->isFresh() || $debug) {
102
            $autoloadCache->write(CodePrinter::print($autoLoadAst) . \PHP_EOL);
103
        }
104
105
        return $autoloadCache->getPath();
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function enterNode(\PhpParser\Node $node)
112
    {
113
        if ($node instanceof Declare_) {
114
            $this->strictDeclare = $node;
115
        } elseif ($node instanceof Class_) {
116
            $indexHash = ($stmtsCount = \count($nodeStmts = $node->stmts)) . 'a';
117
118
            while ($stmtsCount >= $this->maxCount) {
119
                $traitName = 'Definition_' . $this->traitHash($indexHash) . 'Trait';
120
121
                if (null === $this->previousTrait) {
122
                    $stmtsCount = \count($this->traits[$traitName] = \array_splice($nodeStmts, $this->maxCount));
123
                } else {
124
                    $traitStmts = &$this->traits[$this->previousTrait];
125
                    $stmtsCount = \count($this->traits[$traitName] = \array_splice($traitStmts, $this->maxCount));
126
                }
127
128
                $this->previousTrait = $traitName;
129
                ++$indexHash;
130
            }
131
132
            if (empty($this->traits[$this->previousTrait])) {
133
                unset($this->traits[$this->previousTrait]);
134
            }
135
136
            if (!empty($this->traits)) {
137
                $node->stmts = [$this->builder->useTrait(...\array_keys($this->traits))->getNode(), ...$nodeStmts];
138
            }
139
140
            $this->previousTrait = null;
141
        }
142
143
        return parent::enterNode($node);
0 ignored issues
show
Bug introduced by
Are you sure the usage of parent::enterNode($node) targeting PhpParser\NodeVisitorAbstract::enterNode() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
144
    }
145
146
    private function traitHash(string $indexHash): string
147
    {
148
        return \substr(\ucwords(\base64_encode(\hash('sha256', $indexHash))), 0, 7);
149
    }
150
}
151