Completed
Push — master ( 07d3c8...3be31c )
by Greg
04:26
created

FullyQualifiedClassCache::qualify()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
namespace Consolidation\AnnotatedCommand\Parser\Internal;
3
4
class FullyQualifiedClassCache
5
{
6
    protected $classCache = [];
7
    protected $namespaceCache = [];
8
9
    public function qualify($filename, $className)
10
    {
11
        $this->primeCache($filename, $className);
12
        return $this->cached($filename, $className);
13
    }
14
15
    protected function cached($filename, $className)
16
    {
17
        return isset($this->classCache[$filename][$className]) ? $this->classCache[$filename][$className] : $className;
18
    }
19
20
    protected function primeCache($filename, $className)
21
    {
22
        // If the cache has already been primed, do no further work
23
        if (isset($this->namespaceCache[$filename])) {
24
            return false;
25
        }
26
27
        $handle = fopen($filename, "r");
28
        if (!$handle) {
29
            return false;
30
        }
31
32
        $namespaceName = $this->primeNamespaceCache($filename, $handle);
33
        $this->primeUseCache($filename, $handle);
34
35
        // If there is no 'use' statement for the className, then
36
        // generate an effective classname from the namespace
37
        if (!isset($this->classCache[$filename][$className])) {
38
            $this->classCache[$filename][$className] = $namespaceName . '\\' . $className;
39
        }
40
41
        fclose($handle);
42
    }
43
44
    protected function primeNamespaceCache($filename, $handle)
45
    {
46
        $namespaceName = $this->readNamespace($handle);
47
        if (!$namespaceName) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $namespaceName of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
48
            return false;
49
        }
50
        $this->namespaceCache[$filename] = $namespaceName;
51
        return $namespaceName;
52
    }
53
54
    protected function primeUseCache($filename, $handle)
55
    {
56
        $usedClasses = $this->readUseStatements($handle);
57
        if (empty($usedClasses)) {
58
            return false;
59
        }
60
        $this->classCache[$filename] = $usedClasses;
61
    }
62
63
    protected function readNamespace($handle)
64
    {
65
        $namespaceRegex = '#^\s*namespace\s+#';
66
        $line = $this->readNextRelevantLine($handle);
67
        if (!$line || !preg_match($namespaceRegex, $line)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $line of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
68
            return false;
69
        }
70
71
        $namespaceName = preg_replace($namespaceRegex, '', $line);
72
        $namespaceName = rtrim($namespaceName, ';');
73
        return $namespaceName;
74
    }
75
76
    protected function readUseStatements($handle)
77
    {
78
        $useRegex = '#^\s*use\s+#';
79
        $result = [];
80
        while (true) {
81
            $line = $this->readNextRelevantLine($handle);
82
            if (!$line || !preg_match($useRegex, $line)) {
83
                return $result;
84
            }
85
            $usedClass = preg_replace($useRegex, '', $line);
86
            $usedClass = rtrim($usedClass, ';');
87
            $unqualifiedClass = preg_replace('#.*\\\\#', '', $usedClass);
88
            // If this is an aliased class, 'use \Foo\Bar as Baz', then adjust
89
            if (strpos($usedClass, ' as ')) {
90
                $unqualifiedClass = preg_replace('#.*\sas\s+#', '', $usedClass);
91
                $usedClass = preg_replace('#\s+as\s+#', '', $usedClass);
92
            }
93
            $result[$unqualifiedClass] = $usedClass;
94
        }
95
    }
96
97
    protected function readNextRelevantLine($handle)
98
    {
99
        while (($line = fgets($handle)) !== false) {
100
            if (preg_match('#^\s*\w#', $line)) {
101
                return trim($line);
102
            }
103
        }
104
        return false;
105
    }
106
}
107