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
|
|
|
private string $fileName; |
37
|
|
|
private array $traits = []; |
38
|
|
|
private ?string $previousTrait = null; |
39
|
|
|
private \PhpParser\BuilderFactory $builder; |
40
|
|
|
private ?Declare_ $strictDeclare = null; |
41
|
|
|
|
42
|
|
|
public function __construct(int $maxDefinitions = 500, string $fileName = 'definitions_autoload.php') |
43
|
|
|
{ |
44
|
|
|
$this->maxCount = $maxDefinitions; |
45
|
|
|
$this->fileName = $fileName; |
46
|
|
|
$this->builder = new \PhpParser\BuilderFactory(); |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
public function getTraits(): array |
50
|
|
|
{ |
51
|
|
|
return $this->traits; |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Use this function to build generated traits into a cache directory |
56
|
|
|
* and return the file that requires all traits. |
57
|
|
|
* |
58
|
|
|
* @param array<int,string> $includePaths |
59
|
|
|
*/ |
60
|
|
|
public function buildTraits(string $cacheDirectory, bool $debug = false, array $includePaths = []): string |
61
|
|
|
{ |
62
|
|
|
$traitsDirectory = \rtrim($cacheDirectory, '/') . '/Definitions' . $this->traitHash($cacheDirectory); |
63
|
|
|
$autoLoadAst = []; |
64
|
|
|
|
65
|
|
|
if (null !== $this->strictDeclare) { |
66
|
|
|
$autoLoadAst[] = $this->strictDeclare; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
foreach ($includePaths as $bPath) { |
70
|
|
|
$autoLoadAst[] = new Expression(new Include_(new String_($bPath), Include_::TYPE_REQUIRE)); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
foreach ($this->traits as $traitName => $traitStmts) { |
74
|
|
|
$cache = new ConfigCache($traitsDirectory . '/' . $traitName . '.php', $debug); |
75
|
|
|
$autoLoadInclude = new Expression(new Include_(new String_($cache->getPath()), Include_::TYPE_REQUIRE)); |
76
|
|
|
|
77
|
|
|
if (\count($autoLoadAst) <= 1) { |
78
|
|
|
$autoLoadInclude->setDocComment(new \PhpParser\Comment\Doc(\str_replace('class', 'file', CodePrinter::COMMENT))); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
if (!$cache->isFresh() || $debug) { |
82
|
|
|
$traitAst = []; |
83
|
|
|
$traitComment = "\n *\n" . ' * @property array<int,mixed> $services' . \PHP_EOL . ' * @property array<int,mixed> $privates'; |
84
|
|
|
|
85
|
|
|
if (null !== $this->strictDeclare) { |
86
|
|
|
$traitAst[] = $this->strictDeclare; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
$traitAst[] = $this->builder->trait($traitName) |
90
|
|
|
->setDocComment(\strtr(CodePrinter::COMMENT, ['class' => 'trait', '.' => '.' . $traitComment . \PHP_EOL])) |
91
|
|
|
->addStmts($traitStmts) |
92
|
|
|
->getNode(); |
93
|
|
|
|
94
|
|
|
$cache->write(CodePrinter::print($traitAst)); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
$autoLoadAst[] = $autoLoadInclude; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
$autoloadCache = new ConfigCache(\rtrim($cacheDirectory, '/') . '/' . $this->fileName, $debug); |
101
|
|
|
|
102
|
|
|
if (!$autoloadCache->isFresh() || $debug) { |
103
|
|
|
$autoloadCache->write(CodePrinter::print($autoLoadAst) . \PHP_EOL); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
return $autoloadCache->getPath(); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* {@inheritdoc} |
111
|
|
|
*/ |
112
|
|
|
public function enterNode(\PhpParser\Node $node) |
113
|
|
|
{ |
114
|
|
|
if ($node instanceof Declare_) { |
115
|
|
|
$this->strictDeclare = $node; |
116
|
|
|
} elseif ($node instanceof Class_) { |
117
|
|
|
$indexHash = ($stmtsCount = \count($nodeStmts = $node->stmts)) . 'a'; |
118
|
|
|
|
119
|
|
|
while ($stmtsCount >= $this->maxCount) { |
120
|
|
|
$traitName = 'Definition_' . $this->traitHash($indexHash) . 'Trait'; |
121
|
|
|
|
122
|
|
|
if (null === $this->previousTrait) { |
123
|
|
|
$stmtsCount = \count($this->traits[$traitName] = \array_splice($nodeStmts, $this->maxCount)); |
124
|
|
|
} else { |
125
|
|
|
$traitStmts = &$this->traits[$this->previousTrait]; |
126
|
|
|
$stmtsCount = \count($this->traits[$traitName] = \array_splice($traitStmts, $this->maxCount)); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
$this->previousTrait = $traitName; |
130
|
|
|
++$indexHash; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
if (empty($this->traits[$this->previousTrait])) { |
134
|
|
|
unset($this->traits[$this->previousTrait]); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
if (!empty($this->traits)) { |
138
|
|
|
$node->stmts = [$this->builder->useTrait(...\array_keys($this->traits))->getNode(), ...$nodeStmts]; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
$this->previousTrait = null; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
return parent::enterNode($node); |
|
|
|
|
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
private function traitHash(string $indexHash): string |
148
|
|
|
{ |
149
|
|
|
return \substr(\ucwords(\base64_encode(\hash('sha256', $indexHash))), 0, 7); |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
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.