Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
 
3
namespace PhpOffice\PhpSpreadsheet\Calculation\Engine;
4
 
5
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
6
 
7
class BranchPruner
8
{
9
    protected bool $branchPruningEnabled;
10
 
11
    /**
12
     * Used to generate unique store keys.
13
     */
14
    private int $branchStoreKeyCounter = 0;
15
 
16
    /**
17
     * currently pending storeKey (last item of the storeKeysStack.
18
     */
19
    protected ?string $pendingStoreKey = null;
20
 
21
    /**
22
     * @var string[]
23
     */
24
    protected array $storeKeysStack = [];
25
 
26
    /**
27
     * @var bool[]
28
     */
29
    protected array $conditionMap = [];
30
 
31
    /**
32
     * @var bool[]
33
     */
34
    protected array $thenMap = [];
35
 
36
    /**
37
     * @var bool[]
38
     */
39
    protected array $elseMap = [];
40
 
41
    /**
42
     * @var int[]
43
     */
44
    protected array $braceDepthMap = [];
45
 
46
    protected ?string $currentCondition = null;
47
 
48
    protected ?string $currentOnlyIf = null;
49
 
50
    protected ?string $currentOnlyIfNot = null;
51
 
52
    protected ?string $previousStoreKey = null;
53
 
54
    public function __construct(bool $branchPruningEnabled)
55
    {
56
        $this->branchPruningEnabled = $branchPruningEnabled;
57
    }
58
 
59
    public function clearBranchStore(): void
60
    {
61
        $this->branchStoreKeyCounter = 0;
62
    }
63
 
64
    public function initialiseForLoop(): void
65
    {
66
        $this->currentCondition = null;
67
        $this->currentOnlyIf = null;
68
        $this->currentOnlyIfNot = null;
69
        $this->previousStoreKey = null;
70
        $this->pendingStoreKey = empty($this->storeKeysStack) ? null : end($this->storeKeysStack);
71
 
72
        if ($this->branchPruningEnabled) {
73
            $this->initialiseCondition();
74
            $this->initialiseThen();
75
            $this->initialiseElse();
76
        }
77
    }
78
 
79
    private function initialiseCondition(): void
80
    {
81
        if (isset($this->conditionMap[$this->pendingStoreKey]) && $this->conditionMap[$this->pendingStoreKey]) {
82
            $this->currentCondition = $this->pendingStoreKey;
83
            $stackDepth = count($this->storeKeysStack);
84
            if ($stackDepth > 1) {
85
                // nested if
86
                $this->previousStoreKey = $this->storeKeysStack[$stackDepth - 2];
87
            }
88
        }
89
    }
90
 
91
    private function initialiseThen(): void
92
    {
93
        if (isset($this->thenMap[$this->pendingStoreKey]) && $this->thenMap[$this->pendingStoreKey]) {
94
            $this->currentOnlyIf = $this->pendingStoreKey;
95
        } elseif (
96
            isset($this->previousStoreKey, $this->thenMap[$this->previousStoreKey])
97
            && $this->thenMap[$this->previousStoreKey]
98
        ) {
99
            $this->currentOnlyIf = $this->previousStoreKey;
100
        }
101
    }
102
 
103
    private function initialiseElse(): void
104
    {
105
        if (isset($this->elseMap[$this->pendingStoreKey]) && $this->elseMap[$this->pendingStoreKey]) {
106
            $this->currentOnlyIfNot = $this->pendingStoreKey;
107
        } elseif (
108
            isset($this->previousStoreKey, $this->elseMap[$this->previousStoreKey])
109
            && $this->elseMap[$this->previousStoreKey]
110
        ) {
111
            $this->currentOnlyIfNot = $this->previousStoreKey;
112
        }
113
    }
114
 
115
    public function decrementDepth(): void
116
    {
117
        if (!empty($this->pendingStoreKey)) {
118
            --$this->braceDepthMap[$this->pendingStoreKey];
119
        }
120
    }
121
 
122
    public function incrementDepth(): void
123
    {
124
        if (!empty($this->pendingStoreKey)) {
125
            ++$this->braceDepthMap[$this->pendingStoreKey];
126
        }
127
    }
128
 
129
    public function functionCall(string $functionName): void
130
    {
131
        if ($this->branchPruningEnabled && ($functionName === 'IF(')) {
132
            // we handle a new if
133
            $this->pendingStoreKey = $this->getUnusedBranchStoreKey();
134
            $this->storeKeysStack[] = $this->pendingStoreKey;
135
            $this->conditionMap[$this->pendingStoreKey] = true;
136
            $this->braceDepthMap[$this->pendingStoreKey] = 0;
137
        } elseif (!empty($this->pendingStoreKey) && array_key_exists($this->pendingStoreKey, $this->braceDepthMap)) {
138
            // this is not an if but we go deeper
139
            ++$this->braceDepthMap[$this->pendingStoreKey];
140
        }
141
    }
142
 
143
    public function argumentSeparator(): void
144
    {
145
        if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === 0) {
146
            // We must go to the IF next argument
147
            if ($this->conditionMap[$this->pendingStoreKey]) {
148
                $this->conditionMap[$this->pendingStoreKey] = false;
149
                $this->thenMap[$this->pendingStoreKey] = true;
150
            } elseif ($this->thenMap[$this->pendingStoreKey]) {
151
                $this->thenMap[$this->pendingStoreKey] = false;
152
                $this->elseMap[$this->pendingStoreKey] = true;
153
            } elseif ($this->elseMap[$this->pendingStoreKey]) {
154
                throw new Exception('Reaching fourth argument of an IF');
155
            }
156
        }
157
    }
158
 
159
    public function closingBrace(mixed $value): void
160
    {
161
        if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === -1) {
162
            // we are closing an IF(
163
            if ($value !== 'IF(') {
164
                throw new Exception('Parser bug we should be in an "IF("');
165
            }
166
 
167
            if ($this->conditionMap[$this->pendingStoreKey]) {
168
                throw new Exception('We should not be expecting a condition');
169
            }
170
 
171
            $this->thenMap[$this->pendingStoreKey] = false;
172
            $this->elseMap[$this->pendingStoreKey] = false;
173
            --$this->braceDepthMap[$this->pendingStoreKey];
174
            array_pop($this->storeKeysStack);
175
            $this->pendingStoreKey = null;
176
        }
177
    }
178
 
179
    public function currentCondition(): ?string
180
    {
181
        return $this->currentCondition;
182
    }
183
 
184
    public function currentOnlyIf(): ?string
185
    {
186
        return $this->currentOnlyIf;
187
    }
188
 
189
    public function currentOnlyIfNot(): ?string
190
    {
191
        return $this->currentOnlyIfNot;
192
    }
193
 
194
    private function getUnusedBranchStoreKey(): string
195
    {
196
        $storeKeyValue = 'storeKey-' . $this->branchStoreKeyCounter;
197
        ++$this->branchStoreKeyCounter;
198
 
199
        return $storeKeyValue;
200
    }
201
}