Passed
Push — master ( 2dfc84...94e87c )
by Edward
05:17
created

PropertyBuilder::buildScripts()   B

Complexity

Conditions 11
Paths 38

Size

Total Lines 70
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 48
c 1
b 0
f 0
nc 38
nop 1
dl 0
loc 70
rs 7.3166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Remorhaz\UniLex\RegExp;
6
7
use PhpParser\BuilderFactory;
8
use PhpParser\Comment\Doc;
9
use PhpParser\Node\Scalar\LNumber;
10
use PhpParser\Node\Stmt\Declare_;
11
use PhpParser\Node\Stmt\DeclareDeclare;
12
use PhpParser\Node\Stmt\Return_;
13
use ReflectionClass;
14
use ReflectionException;
15
use Remorhaz\UniLex\Console\PrettyPrinter;
16
use Remorhaz\UniLex\Exception as UniLexException;
17
use Remorhaz\UniLex\RegExp\FSM\Range;
18
use Remorhaz\UniLex\RegExp\FSM\RangeSet;
19
use RuntimeException;
20
use SplFileObject;
21
22
use function explode;
23
use function file_put_contents;
24
use function hexdec;
25
use function trim;
26
27
final class PropertyBuilder
28
{
29
30
    private const PROP_DIR = '/Properties';
31
32
    private $phpBuilder;
33
34
    private $printer;
35
36
    public function __construct()
37
    {
38
        $this->phpBuilder = new BuilderFactory();
39
        $this->printer = new PrettyPrinter();
40
    }
41
42
    /**
43
     * @param array $index
44
     * @return array
45
     * @throws UniLexException
46
     * @throws ReflectionException
47
     */
48
    public function buildScripts(array $index): array
49
    {
50
        /** @var RangeSet[] $ranges */
51
        $ranges = [];
52
        $source = new SplFileObject(__DIR__ . '/../../data/Scripts.txt');
53
        $lastKnownCode = null;
54
        $unknownRanges = [];
55
56
        echo "Parsing: ";
57
        $rangeCount = 0;
58
59
        while (!$source->eof()) {
60
            $line = $source->fgets();
61
            if (false === $line) {
62
                throw new RuntimeException("Error reading line from scripts file");
63
            }
64
            $dataWithComment = explode('#', $line, 2);
65
            $data = trim($dataWithComment[0] ?? '');
66
            if ('' == $data) {
67
                continue;
68
            }
69
            $rangeWithProp = explode(';', $data);
70
            $unsplittedRange = trim($rangeWithProp[0] ?? null);
71
            $prop = trim($rangeWithProp[1] ?? null);
72
            if (!isset($unsplittedRange, $prop)) {
73
                throw new RuntimeException("Invalid range or property");
74
            }
75
            $splittedRange = explode('..', $unsplittedRange);
76
            $start = hexdec($splittedRange[0]);
77
            $finish = isset($splittedRange[1])
78
                ? hexdec($splittedRange[1])
79
                : $start;
80
            if (!isset($lastKnownCode)) {
81
                if ($start > 0) {
82
                    $unknownRanges[] = new Range(0, $start - 1);
0 ignored issues
show
Bug introduced by
$start - 1 of type double is incompatible with the type integer|null expected by parameter $finish of Remorhaz\UniLex\RegExp\FSM\Range::__construct(). ( Ignorable by Annotation )

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

82
                    $unknownRanges[] = new Range(0, /** @scrutinizer ignore-type */ $start - 1);
Loading history...
83
                }
84
            } elseif ($start - $lastKnownCode > 1) {
85
                $unknownRanges[] = new Range($lastKnownCode + 1, $start - 1);
86
            }
87
            $lastKnownCode = $finish;
88
89
            if (!isset($ranges[$prop])) {
90
                $ranges[$prop] = [];
91
            }
92
            $range = new Range($start, $finish);
0 ignored issues
show
Bug introduced by
It seems like $start can also be of type double; however, parameter $start of Remorhaz\UniLex\RegExp\FSM\Range::__construct() does only seem to accept integer, 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

92
            $range = new Range(/** @scrutinizer ignore-type */ $start, $finish);
Loading history...
93
            $ranges[$prop][] = $range;
94
            echo ".";
95
            $rangeCount++;
96
        }
97
        $ranges['Unknown'] = $unknownRanges;
98
        echo ". {$rangeCount} ranges\n";
99
        $source = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $source is dead and can be removed.
Loading history...
100
101
        echo "Building range sets: ";
102
        $rangeSetCount = 0;
103
        $rangeSets = [];
104
        foreach ($ranges as $prop => $rangeList) {
105
            $rangeSets[$prop] = new RangeSet(...$rangeList);
106
            echo ".";
107
            $rangeSetCount++;
108
        }
109
        echo " {$rangeSetCount} range sets\n";
110
111
        /*echo "Build Unknown property...";
112
        $fullRangeSet = new RangeSet(new Range(0x0, 0x10FFFF));
113
        $knownRangeSet = new RangeSet(...$knownRanges);
114
        $unknownRangeSet = $rangeSetCalc->xor($fullRangeSet, $knownRangeSet);
115
        $ranges['Unknown'] = $unknownRangeSet;*/
116
117
        return $this->dumpProps($index, $rangeSets);
118
    }
119
120
    /**
121
     * @param array $index
122
     * @param array $rangeSets
123
     * @return array
124
     * @throws ReflectionException
125
     */
126
    private function dumpProps(array $index, array $rangeSets): array
127
    {
128
        $rangeSetClass = new ReflectionClass(RangeSet::class);
129
        $rangeClass = new ReflectionClass(Range::class);
130
        foreach ($rangeSets as $prop => $rangeSet) {
131
            $targetFile = self::PROP_DIR . "/{$prop}.php";
132
133
            $phpNodes = [];
134
            $declare = new Declare_([new DeclareDeclare('strict_types', $this->phpBuilder->val(1))]);
135
            $declare->setDocComment(new Doc('/** @noinspection PhpUnhandledExceptionInspection */'));
136
            $phpNodes[] = $declare;
137
            $phpNodes[] = $this->phpBuilder->namespace(__NAMESPACE__ . '\\Properties')->getNode();
138
            $phpNodes[] = $this->phpBuilder->use($rangeClass->getName())->getNode();
139
            $phpNodes[] = $this->phpBuilder->use($rangeSetClass->getName())->getNode();
140
            $phpRanges = [];
141
142
            foreach ($rangeSet->getRanges() as $range) {
143
                $rangeStart = $range->getStart();
144
                $rangeFinish = $range->getFinish();
145
                $phpRangeStart = $this->phpBuilder->val($rangeStart);
146
                $phpRangeStart->setAttribute('kind', LNumber::KIND_HEX);
147
                $phpRangeArgs = [$phpRangeStart];
148
                if ($rangeStart != $rangeFinish) {
149
                    $phpRangeFinish = $this->phpBuilder->val($rangeFinish);
150
                    $phpRangeFinish->setAttribute('kind', LNumber::KIND_HEX);
151
                    $phpRangeArgs[] = $phpRangeFinish;
152
                }
153
                $phpRanges[] = $this->phpBuilder->new($rangeClass->getShortName(), $phpRangeArgs);
154
            }
155
            $phpReturn = new Return_(
156
                $this->phpBuilder->staticCall($rangeSetClass->getShortName(), 'loadUnsafe', $phpRanges)
157
            );
158
            $phpReturn->setDocComment(new Doc('/** phpcs:disable Generic.Files.LineLength.TooLong */'));
159
            $phpNodes[] = $phpReturn;
160
            file_put_contents(__DIR__ . $targetFile, $this->printer->prettyPrintFile($phpNodes));
161
            $index[$prop] = $targetFile;
162
        }
163
164
        return $index;
165
    }
166
167
    public function dumpIndex(array $index): void
168
    {
169
        $indexCode = "<?php\n\nreturn " . var_export($index, true) . ";\n";
170
        file_put_contents(__DIR__ . '/PropertyIndex.php', $indexCode);
171
    }
172
}
173