BulkDeleteAutoloader::load_class()   A
last analyzed

Complexity

Conditions 5
Paths 7

Size

Total Lines 33
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.246

Importance

Changes 0
Metric Value
cc 5
eloc 13
c 0
b 0
f 0
nc 7
nop 1
dl 0
loc 33
ccs 11
cts 14
cp 0.7856
crap 5.246
rs 9.5222
1
<?php namespace BulkWP\BulkDelete;
2
3
/**
4
 * Autoloader for Bulk Delete, based on the PSR-4 general purpose implementation.
5
 *
6
 * @see http://www.php-fig.org/psr/psr-4/
7
 *
8
 * This differs from WordPress coding standard in the following ways.
9
 *
10
 * - Class name and directory names use Snake case.
11
 * - Use of namespaces.
12
 *
13
 * Given a foo-bar package of classes in the file system at the following
14
 * paths ...
15
 *
16
 *     /path/to/packages/foo-bar/
17
 *         src/
18
 *             Baz.php             # Foo\Bar\Baz
19
 *             Qux/
20
 *                 Quux.php        # Foo\Bar\Qux\Quux
21
 *         tests/
22
 *             BazTest.php         # Foo\Bar\BazTest
23
 *             Qux/
24
 *                 QuuxTest.php    # Foo\Bar\Qux\QuuxTest
25
 *
26
 * ... add the path to the class files for the \Foo\Bar\ namespace prefix
27
 * as follows:
28
 *
29
 *      <?php
30
 *      // instantiate the loader
31
 *      $loader = new \BulkWP\BulkDelete\BulkDeleteAutoloader;
32
 *
33
 *      // register the autoloader
34
 *      $loader->register();
35
 *
36
 *      // register the base directories for the namespace prefix
37
 *      $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/src');
38
 *      $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/tests');
39
 *
40
 * The following line would cause the autoloader to attempt to load the
41
 * \Foo\Bar\Qux\Quux class from /path/to/packages/foo-bar/src/Qux/Quux.php:
42
 *
43
 *      <?php
44
 *      new \Foo\Bar\Qux\Quux;
45
 *
46
 * The following line would cause the autoloader to attempt to load the
47
 * \Foo\Bar\Qux\QuuxTest class from /path/to/packages/foo-bar/tests/Qux/QuuxTest.php:
48
 *
49
 *      <?php
50
 *      new \Foo\Bar\Qux\QuuxTest;
51
 * @since 6.0.0
52
 */
53
class BulkDeleteAutoloader {
54
	/**
55
	 * An associative array where the key is a namespace prefix and the value
56
	 * is an array of base directories for classes in that namespace.
57
	 *
58
	 * @var array
59
	 */
60
	protected $prefixes = array();
61
62
	/**
63
	 * An associative array containing the list files that needs to be autoloaded.
64
	 *
65
	 * @var array
66
	 */
67
	protected $files = array();
68
69
	protected $custom_class_map = array();
70
71
	/**
72
	 * Register loader with SPL autoloader stack.
73
	 *
74
	 * @return void
75
	 */
76
	public function register() {
77
		spl_autoload_register( array( $this, 'load_class' ) );
78
79
		// file exists check is already done in `add_file`.
80
		foreach ( $this->files as $file ) {
81
			$this->require_file( $file );
82
		}
83
	}
84
85
	/**
86
	 * Adds a base directory for a namespace prefix.
87
	 *
88
	 * @param string $prefix   The namespace prefix.
89
	 * @param string $base_dir A base directory for class files in the
90
	 *                         namespace.
91
	 * @param bool   $prepend  If true, prepend the base directory to the stack
92
	 *                         instead of appending it; this causes it to be searched first rather
93
	 *                         than last.
94
	 *
95
	 * @return void
96
	 */
97
	public function add_namespace( $prefix, $base_dir, $prepend = false ) {
98
		// normalize namespace prefix.
99
		$prefix = trim( $prefix, '\\' ) . '\\';
100
101
		// normalize the base directory with a trailing separator.
102
		$base_dir = rtrim( $base_dir, DIRECTORY_SEPARATOR ) . '/';
103
104
		// initialize the namespace prefix array.
105
		if ( false === isset( $this->prefixes[ $prefix ] ) ) {
106
			$this->prefixes[ $prefix ] = array();
107
		}
108
109
		// retain the base directory for the namespace prefix.
110
		if ( $prepend ) {
111
			array_unshift( $this->prefixes[ $prefix ], $base_dir );
112
		} else {
113
			array_push( $this->prefixes[ $prefix ], $base_dir );
114
		}
115
	}
116
117
	/**
118
	 * Add a file to be autoloaded.
119
	 *
120
	 * @param string $filename File to be autoloaded.
121
	 */
122
	public function add_file( $filename ) {
123
		if ( ! in_array( $filename, $this->files, true ) ) {
124
			$this->files[] = $filename;
125
		}
126
	}
127
128
	/**
129
	 * Loads the class file for a given class name.
130
	 *
131
	 * @param string $class The fully-qualified class name.
132
	 *
133
	 * @return false|string The mapped file name on success, or boolean false on
134
	 *                      failure.
135
	 */
136 293
	public function load_class( $class ) {
137 293
		if ( array_key_exists( $class, $this->custom_class_map ) ) {
138
			$file_loaded = $this->require_file( $this->custom_class_map[ $class ] );
139
140
			if ( $file_loaded ) {
141
				return true;
142
			}
143
		}
144
145
		// the current namespace prefix.
146 293
		$prefix = $class;
147
148
		// work backwards through the namespace names of the fully-qualified class name to find a mapped file name.
149 293
		while ( false !== $pos = strrpos( $prefix, '\\' ) ) {
150
			// retain the trailing namespace separator in the prefix.
151 18
			$prefix = substr( $class, 0, $pos + 1 );
152
153
			// the rest is the relative class name.
154 18
			$relative_class = substr( $class, $pos + 1 );
155
156
			// try to load a mapped file for the prefix and relative class.
157 18
			$mapped_file = $this->load_mapped_file( $prefix, $relative_class );
158 18
			if ( $mapped_file !== false ) {
159 18
				return $mapped_file;
160
			}
161
162
			// remove the trailing namespace separator for the next iteration
163
			// of strrpos().
164 18
			$prefix = rtrim( $prefix, '\\' );
165
		}
166
167
		// never found a mapped file.
168 287
		return false;
169
	}
170
171
	/**
172
	 * Load the mapped file for a namespace prefix and relative class.
173
	 *
174
	 * @param string $prefix         The namespace prefix.
175
	 * @param string $relative_class The relative class name.
176
	 *
177
	 * @return false|string Boolean false if no mapped file can be loaded, or the
178
	 *                      name of the mapped file that was loaded.
179
	 */
180 18
	protected function load_mapped_file( $prefix, $relative_class ) {
181
		// are there any base directories for this namespace prefix?
182 18
		if ( false === isset( $this->prefixes[ $prefix ] ) ) {
183 18
			return false;
184
		}
185
186
		// look through base directories for this namespace prefix.
187 18
		foreach ( $this->prefixes[ $prefix ] as $base_dir ) {
188
			// replace the namespace prefix with the base directory,
189
			// replace namespace separators with directory separators
190
			// in the relative class name, append with .php.
191 18
			$file = $base_dir . str_replace( '\\', '/', $relative_class ) . '.php';
192
193
			// if the mapped file exists, require it.
194 18
			if ( $this->require_file( $file ) ) {
195
				// yes, we're done.
196 18
				return $file;
197
			}
198
		}
199
200
		// never found it.
201
		return false;
202
	}
203
204
	/**
205
	 * If a file exists, require it from the file system.
206
	 *
207
	 * @param string $file The file to require.
208
	 *
209
	 * @return bool True if the file exists, false if not.
210
	 */
211 18
	protected function require_file( $file ) {
212 18
		if ( file_exists( $file ) ) {
213 18
			require_once $file;
214
215 18
			return true;
216
		}
217
218
		return false;
219
	}
220
221
	public function set_custom_mapping( $custom_class_map ) {
222
		$this->custom_class_map = $custom_class_map;
223
	}
224
}
225