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

DefinitionsSplitter::getTraits()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
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
     * @param array<int,string> $includePaths
64
     */
65
    public function buildTraits(string $cacheDirectory, bool $debug = false, array $includePaths = []): string
66
    {
67
        $traitsDirectory = \rtrim($cacheDirectory, '/') . '/Definitions' . $this->traitHash($cacheDirectory);
68
        $autoLoadAst = [];
69
70
        if (null !== $this->strictDeclare) {
71
            $autoLoadAst[] = $this->strictDeclare;
72
        }
73
74
        foreach ($includePaths as $bPath) {
75
            $autoLoadAst[] = new Expression(new Include_(new String_($bPath), Include_::TYPE_REQUIRE));
76
        }
77
78
        foreach ($this->traits as $traitName => $traitStmts) {
79
            $cache = new ConfigCache($traitsDirectory . '/' . $traitName . '.php', $debug);
80
            $autoLoadInclude = new Expression(new Include_(new String_($cache->getPath()), Include_::TYPE_REQUIRE));
81
82
            if (\count($autoLoadAst) <= 1) {
83
                $autoLoadInclude->setDocComment(new \PhpParser\Comment\Doc(\str_replace('class', 'file', CodePrinter::COMMENT)));
84
            }
85
86
            if (!$cache->isFresh() || $debug) {
87
                $traitAst = [];
88
                $traitComment = "\n *\n" . ' * @property array<int,mixed> $services' . \PHP_EOL . ' * @property array<int,mixed> $privates';
89
90
                if (null !== $this->strictDeclare) {
91
                    $traitAst[] = $this->strictDeclare;
92
                }
93
94
                $traitAst[] = $this->builder->trait($traitName)
95
                    ->setDocComment(\strtr(CodePrinter::COMMENT, ['class' => 'trait', '.' => '.' . $traitComment . \PHP_EOL]))
96
                    ->addStmts($traitStmts)
97
                    ->getNode();
98
99
                $cache->write(CodePrinter::print($traitAst));
100
            }
101
102
            $autoLoadAst[] = $autoLoadInclude;
103
        }
104
105
        $autoloadCache = new ConfigCache(\rtrim($cacheDirectory, '/') . '/' . $this->fileName, $debug);
106
107
        if (!$autoloadCache->isFresh() || $debug) {
108
            $autoloadCache->write(CodePrinter::print($autoLoadAst) . \PHP_EOL);
109
        }
110
111
        return $autoloadCache->getPath();
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117
    public function enterNode(\PhpParser\Node $node)
118
    {
119
        if ($node instanceof Declare_) {
120
            $this->strictDeclare = $node;
121
        } elseif ($node instanceof Class_) {
122
            $indexHash = ($stmtsCount = \count($nodeStmts = $node->stmts)) . 'a';
123
124
            while ($stmtsCount >= $this->maxCount) {
125
                $traitName = 'Definition_' . $this->traitHash($indexHash) . 'Trait';
126
127
                if (null === $this->previousTrait) {
128
                    $stmtsCount = \count($this->traits[$traitName] = \array_splice($nodeStmts, $this->maxCount));
129
                } else {
130
                    $traitStmts = &$this->traits[$this->previousTrait];
131
                    $stmtsCount = \count($this->traits[$traitName] = \array_splice($traitStmts, $this->maxCount));
132
                }
133
134
                $this->previousTrait = $traitName;
135
                ++$indexHash;
136
            }
137
138
            if (empty($this->traits[$this->previousTrait])) {
139
                unset($this->traits[$this->previousTrait]);
140
            }
141
142
            if (!empty($this->traits)) {
143
                $node->stmts = [$this->builder->useTrait(...\array_keys($this->traits))->getNode(), ...$nodeStmts];
144
            }
145
146
            $this->previousTrait = null;
147
        }
148
149
        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...
150
    }
151
152
    private function traitHash(string $indexHash): string
153
    {
154
        return \substr(\ucwords(\base64_encode(\hash('sha256', $indexHash))), 0, 7);
155
    }
156
}
157