GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

PharInvocationResolver   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 223
Duplicated Lines 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 88
c 6
b 0
f 0
dl 0
loc 223
rs 9.6
wmc 35

8 Methods

Rating   Name   Duplication   Size   Complexity  
A resolvePossibleAlias() 0 4 2
A retrieveInvocation() 0 16 3
A addBaseName() 0 7 2
A findInBaseNames() 0 18 4
A findByBaseName() 0 7 1
C resolveBaseName() 0 48 14
A findByAlias() 0 11 3
A resolve() 0 22 6
1
<?php
2
declare(strict_types=1);
3
namespace TYPO3\PharStreamWrapper\Resolver;
4
5
/*
6
 * This file is part of the TYPO3 project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under the terms
9
 * of the MIT License (MIT). For the full copyright and license information,
10
 * please read the LICENSE file that was distributed with this source code.
11
 *
12
 * The TYPO3 project - inspiring people to share!
13
 */
14
15
use TYPO3\PharStreamWrapper\Helper;
16
use TYPO3\PharStreamWrapper\Manager;
17
use TYPO3\PharStreamWrapper\Phar\Reader;
18
use TYPO3\PharStreamWrapper\Phar\ReaderException;
19
use TYPO3\PharStreamWrapper\Resolvable;
20
21
class PharInvocationResolver implements Resolvable
22
{
23
    const RESOLVE_REALPATH = 1;
24
    const RESOLVE_ALIAS = 2;
25
    const ASSERT_INTERNAL_INVOCATION = 32;
26
27
    /**
28
     * @var string[]
29
     */
30
    private $invocationFunctionNames = [
31
        'include',
32
        'include_once',
33
        'require',
34
        'require_once'
35
    ];
36
37
    /**
38
     * Contains resolved base names in order to reduce file IO.
39
     *
40
     * @var string[]
41
     */
42
    private $baseNames = [];
43
44
    /**
45
     * Resolves PharInvocation value object (baseName and optional alias).
46
     *
47
     * Phar aliases are intended to be used only inside Phar archives, however
48
     * PharStreamWrapper needs this information exposed outside of Phar as well
49
     * It is possible that same alias is used for different $baseName values.
50
     * That's why PharInvocationCollection behaves like a stack when resolving
51
     * base-name for a given alias. On the other hand it is not possible that
52
     * one $baseName is referring to multiple aliases.
53
     * @see https://secure.php.net/manual/en/phar.setalias.php
54
     * @see https://secure.php.net/manual/en/phar.mapphar.php
55
     *
56
     * @param string $path
57
     * @param int|null $flags
58
     * @return null|PharInvocation
59
     */
60
    public function resolve(string $path, int $flags = null)
61
    {
62
        $hasPharPrefix = Helper::hasPharPrefix($path);
63
        $flags = $flags ?? static::RESOLVE_REALPATH | static::RESOLVE_ALIAS;
64
65
        if ($hasPharPrefix && $flags & static::RESOLVE_ALIAS) {
66
            $invocation = $this->findByAlias($path);
67
            if ($invocation !== null) {
68
                return $invocation;
69
            }
70
        }
71
72
        $baseName = $this->resolveBaseName($path, $flags);
73
        if ($baseName === null) {
74
            return null;
75
        }
76
77
        if ($flags & static::RESOLVE_REALPATH) {
78
            $baseName = $this->baseNames[$baseName];
79
        }
80
81
        return $this->retrieveInvocation($baseName, $flags);
82
    }
83
84
    /**
85
     * Retrieves PharInvocation, either existing in collection or created on demand
86
     * with resolving a potential alias name used in the according Phar archive.
87
     *
88
     * @param string $baseName
89
     * @param int $flags
90
     * @return PharInvocation
91
     */
92
    private function retrieveInvocation(string $baseName, int $flags): PharInvocation
93
    {
94
        $invocation = $this->findByBaseName($baseName);
95
        if ($invocation !== null) {
96
            return $invocation;
97
        }
98
99
        if ($flags & static::RESOLVE_ALIAS) {
100
            $alias = (new Reader($baseName))->resolveContainer()->getAlias();
101
        } else {
102
            $alias = '';
103
        }
104
        // add unconfirmed(!) new invocation to collection
105
        $invocation = new PharInvocation($baseName, $alias);
106
        Manager::instance()->getCollection()->collect($invocation);
107
        return $invocation;
108
    }
109
110
    /**
111
     * @param string $path
112
     * @param int $flags
113
     * @return null|string
114
     */
115
    private function resolveBaseName(string $path, int $flags)
116
    {
117
        $baseName = $this->findInBaseNames($path);
118
        if ($baseName !== null) {
119
            return $baseName;
120
        }
121
122
        $baseName = Helper::determineBaseFile($path);
123
        if ($baseName !== null) {
124
            $this->addBaseName($baseName);
125
            return $baseName;
126
        }
127
128
        $possibleAlias = $this->resolvePossibleAlias($path);
129
        if (!($flags & static::RESOLVE_ALIAS) || $possibleAlias === null) {
130
            return null;
131
        }
132
133
        $trace = debug_backtrace();
134
        foreach ($trace as $item) {
135
            if (!isset($item['function']) || !isset($item['args'][0])
136
                || !in_array($item['function'], $this->invocationFunctionNames, true)) {
137
                continue;
138
            }
139
            $currentPath = $item['args'][0];
140
            if (Helper::hasPharPrefix($currentPath)) {
141
                continue;
142
            }
143
            $currentBaseName = Helper::determineBaseFile($currentPath);
144
            if ($currentBaseName === null) {
145
                continue;
146
            }
147
            // ensure the possible alias name (how we have been called initially) matches
148
            // the resolved alias name that was retrieved by the current possible base name
149
            try {
150
                $currentAlias = (new Reader($currentBaseName))->resolveContainer()->getAlias();
151
            } catch (ReaderException $exception) {
152
                // most probably that was not a Phar file
153
                continue;
154
            }
155
            if (empty($currentAlias) || $currentAlias !== $possibleAlias) {
156
                continue;
157
            }
158
            $this->addBaseName($currentBaseName);
159
            return $currentBaseName;
160
        }
161
162
        return null;
163
    }
164
165
    /**
166
     * @param string $path
167
     * @return null|string
168
     */
169
    private function resolvePossibleAlias(string $path)
170
    {
171
        $normalizedPath = Helper::normalizePath($path);
172
        return strstr($normalizedPath, '/', true) ?: null;
173
    }
174
175
    /**
176
     * @param string $baseName
177
     * @return null|PharInvocation
178
     */
179
    private function findByBaseName(string $baseName)
180
    {
181
        return Manager::instance()->getCollection()->findByCallback(
182
            function (PharInvocation $candidate) use ($baseName) {
183
                return $candidate->getBaseName() === $baseName;
184
            },
185
            true
186
        );
187
    }
188
189
    /**
190
     * @param string $path
191
     * @return null|string
192
     */
193
    private function findInBaseNames(string $path)
194
    {
195
        // return directly if the resolved base name was submitted
196
        if (in_array($path, $this->baseNames, true)) {
197
            return $path;
198
        }
199
200
        $parts = explode('/', Helper::normalizePath($path));
201
202
        while (count($parts)) {
203
            $currentPath = implode('/', $parts);
204
            if (isset($this->baseNames[$currentPath])) {
205
                return $currentPath;
206
            }
207
            array_pop($parts);
208
        }
209
210
        return null;
211
    }
212
213
    /**
214
     * @param string $baseName
215
     */
216
    private function addBaseName(string $baseName)
217
    {
218
        if (isset($this->baseNames[$baseName])) {
219
            return;
220
        }
221
        $this->baseNames[$baseName] = Helper::normalizeWindowsPath(
222
            realpath($baseName)
223
        );
224
    }
225
226
    /**
227
     * Finds confirmed(!) invocations by alias.
228
     *
229
     * @param string $path
230
     * @return null|PharInvocation
231
     * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation()
232
     */
233
    private function findByAlias(string $path)
234
    {
235
        $possibleAlias = $this->resolvePossibleAlias($path);
236
        if ($possibleAlias === null) {
237
            return null;
238
        }
239
        return Manager::instance()->getCollection()->findByCallback(
240
            function (PharInvocation $candidate) use ($possibleAlias) {
241
                return $candidate->isConfirmed() && $candidate->getAlias() === $possibleAlias;
242
            },
243
            true
244
        );
245
    }
246
}
247