Completed
Push — master ( 5310aa...d15de7 )
by
unknown
01:54
created

Autoloader   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 146
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 0
dl 0
loc 146
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A register() 0 4 1
A addNamespace() 0 11 2
B loadClass() 0 25 3
A loadMappedFile() 0 20 4
A normalizePath() 0 4 1
A getRelativeFile() 0 13 1
A requireFile() 0 8 2
1
<?php
2
/**
3
 * Autoloader
4
 *
5
 * Load namespaces and classes following the WordPress naming convertions.
6
 * https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/#naming-conventions
7
 */
8
namespace ClaudioSanches\WPAutoloader;
9
10
/**
11
 * Autoloader.
12
 */
13
class Autoloader
14
{
15
    /**
16
     * An associative array where the key is a namespace prefix and the value
17
     * is an array of base directories for classes in that namespace.
18
     *
19
     * @var array
20
     */
21
    protected $prefixes = [];
22
23
    /**
24
     * Register loader with SPL autoloader stack.
25
     */
26
    public function register()
27
    {
28
        spl_autoload_register([$this, 'loadClass']);
29
    }
30
31
    /**
32
     * Adds a base directory for a namespace prefix.
33
     *
34
     * @param string $prefix  The namespace prefix.
35
     * @param string $baseDir A base directory for class files in the
36
     * namespace.
37
     */
38
    public function addNamespace(string $prefix, string $baseDir)
39
    {
40
        $prefix  = trim($prefix, '\\') . '\\';
41
        $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
42
43
        if (false === isset($this->prefixes[$prefix])) {
44
            $this->prefixes[$prefix] = [];
45
        }
46
47
        array_push($this->prefixes[$prefix], $baseDir);
48
    }
49
50
    /**
51
     * Loads the class file for a given class name.
52
     *
53
     * @param  string $class The fully-qualified class name.
54
     * @return string        The mapped file name on success, or emtpy string on
55
     * failure.
56
     */
57
    public function loadClass($class): string
58
    {
59
        $prefix = $class;
60
61
        // Work backwards through the namespace names of the fully-qualified
62
        // class name to find a mapped file name.
63
        while (false !== ($position = strrpos($prefix, '\\'))) {
64
            // Tetain the trailing namespace separator in the prefix.
65
            $prefix = substr($class, 0, $position + 1);
66
67
            // The rest is the relative class name.
68
            $relativeClass = substr($class, $position + 1);
69
70
            // Try to load a mapped file for the prefix and relative class.
71
            if ($mappedFile = $this->loadMappedFile($prefix, $relativeClass)) {
72
                return $mappedFile;
73
            }
74
75
            // Remove the trailing namespace separator for the next iteration
76
            // of strrpos().
77
            $prefix = rtrim($prefix, '\\');
78
        }
79
80
        return '';
81
    }
82
83
    /**
84
     * Load the mapped file for a namespace prefix and relative class.
85
     *
86
     * @param string $prefix        The namespace prefix.
87
     * @param string $relativeClass The relative class name.
88
     *
89
     * @return string               Empty string if no mapped file can be
90
     * loaded, or the name of the mapped file that was loaded.
91
     */
92
    protected function loadMappedFile($prefix, $relativeClass)
93
    {
94
        if (false === isset($this->prefixes[$prefix])) {
95
            return '';
96
        }
97
98
        $relativeFile = $this->getRelativeFile($relativeClass);
99
100
        // Look through base directories for this namespace prefix.
101
        foreach ($this->prefixes[$prefix] as $baseDir) {
102
            $file = $baseDir . $relativeFile;
103
104
            // If the mapped file exists, require it.
105
            if ($this->requireFile($file)) {
106
                return $file;
107
            }
108
        }
109
110
        return '';
111
    }
112
113
    /**
114
     * Normalize path using WordPress naming convertions.
115
     *
116
     * @param  string $path Relative class path.
117
     * @return string
118
     */
119
    protected function normalizePath(string $path): string
120
    {
121
        return str_replace('_', '-', strtolower($path));
122
    }
123
124
    /**
125
     * Get relative file path.
126
     *
127
     * @param  string $relativeClass Relative class.
128
     * @return string
129
     */
130
    protected function getRelativeFile(string $relativeClass): string
131
    {
132
        // Class file names should be based on the class name with
133
        // "class-" prepended and the underscores in the class name
134
        // replaced with hyphens.
135
        $relative = $this->normalizePath($relativeClass);
136
        $pieces   = explode('\\', $relative);
137
        $last     = array_pop($pieces);
138
        $last     = 'class-' . $last . '.php';
139
        $pieces[] = $last;
140
141
        return implode(DIRECTORY_SEPARATOR, $pieces);
142
    }
143
144
    /**
145
     * If a file exists, require it from the file system.
146
     *
147
     * @param  string $file The file to require.
148
     * @return bool         True if the file exists, false if not.
149
     */
150
    protected function requireFile(string $file): bool
151
    {
152
        if (file_exists($file)) {
153
            require $file;
154
            return true;
155
        }
156
        return false;
157
    }
158
}
159