Completed
Push — add/autoloader-path-cache ( 8f4e9f...9c201d )
by
unknown
08:04
created

Path_Processor::find_directory_with_autoloader()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 7
nop 2
dl 0
loc 21
rs 9.2728
c 0
b 0
f 0
1
<?php
2
/* HEADER */ // phpcs:ignore
3
4
/**
5
 * This class handles dealing with paths for the autoloader.
6
 */
7
class Path_Processor {
8
	/**
9
	 * Given a path this will replace any of the path constants with a token to represent it.
10
	 *
11
	 * @param string $path The path we want to process.
12
	 * @return string The tokenized path.
13
	 */
14 View Code Duplication
	public function tokenize_path_constants( $path ) {
15
		$path = self::normalize( $path );
16
17
		$constants = self::get_path_constants();
18
		foreach ( $constants as $constant => $constant_path ) {
19
			$len = strlen( $constant_path );
20
			if ( substr( $path, 0, $len ) !== $constant_path ) {
21
				continue;
22
			}
23
24
			$path = substr_replace( $path, '{{' . $constant . '}}', 0, $len );
25
			break;
26
		}
27
28
		return $path;
29
	}
30
31
	/**
32
	 * Given a path this will replace any of the path constant tokens with the expanded path.
33
	 *
34
	 * @param string $path The path we want to process.
35
	 * @return string The expanded path.
36
	 */
37 View Code Duplication
	public function untokenize_path_constants( $path ) {
38
		$constants = self::get_path_constants();
39
		foreach ( $constants as $constant => $constant_path ) {
40
			$constant = '{{' . $constant . '}}';
41
42
			$len = strlen( $constant );
43
			if ( substr( $path, 0, $len ) !== $constant ) {
44
				continue;
45
			}
46
47
			$path = substr_replace( $path, $constant_path, 0, $len );
48
			break;
49
		}
50
51
		return $path;
52
	}
53
54
	/**
55
	 * Given a file and an array of places it might be, this will find the absolute path and return it.
56
	 *
57
	 * @param string $file The plugin or theme file to resolve.
58
	 * @param array  $directories_to_check The directories we should check for the file if it isn't an absolute path.
59
	 * @return string|false Returns the absolute path to the directory, otherwise false.
60
	 */
61
	public function find_directory_with_autoloader( $file, $directories_to_check ) {
62
		$file = self::normalize( $file );
63
64
		// We need an absolute path to the plugin directory to check for an autoloader.
65
		if ( path_is_absolute( $file ) ) {
0 ignored issues
show
Bug introduced by
It seems like $file defined by self::normalize($file) on line 62 can also be of type array<integer,string>; however, path_is_absolute() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
66
			// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
67
			$directory = @is_file( $file ) ? dirname( $file ) : $file;
68
		} else {
69
			$directory = $this->find_plugin_directory( $file, $directories_to_check );
0 ignored issues
show
Bug introduced by
It seems like $file defined by self::normalize($file) on line 62 can also be of type array<integer,string>; however, Path_Processor::find_plugin_directory() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
70
			if ( ! isset( $directory ) ) {
71
				return false;
72
			}
73
		}
74
75
		// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
76
		if ( ! @is_file( $directory . '/vendor/composer/jetpack_autoload_classmap.php' ) ) {
77
			return false;
78
		}
79
80
		return $directory;
81
	}
82
83
	/**
84
	 * Fetches an array of paths keyed by the constant they came from.
85
	 *
86
	 * @return string[] The paths keyed by the constant.
87
	 */
88
	private static function get_path_constants() {
89
		$raw_constants = array(
90
			// Order the constants from most-specific to least-specific.
91
			'WP_PLUGIN_DIR',
92
			'WPMU_PLUGIN_DIR',
93
			'WP_CONTENT_DIR',
94
			'ABSPATH',
95
		);
96
97
		$constants = array();
98
		foreach ( $raw_constants as $raw ) {
99
			if ( ! defined( $raw ) ) {
100
				continue;
101
			}
102
103
			// Normalize the path for consistency.
104
			$path = self::normalize( constant( $raw ) );
105
			if ( isset( $path ) ) {
106
				$constants[ $raw ] = $path;
107
			}
108
		}
109
110
		return $constants;
111
	}
112
113
	/**
114
	 * Given a file and a list of directories to check, this method will try to figure out
115
	 * the absolute path to the file in question.
116
	 *
117
	 * @param string $file The plugin or theme file to resolve.
118
	 * @param array  $directories_to_check The directories we should check for the file if it isn't an absolute path.
119
	 * @return string|null The absolute path to the plugin directory, otherwise null.
120
	 */
121
	private function find_plugin_directory( $file, $directories_to_check ) {
122
		// We're only able to find the absolute path for plugin/theme PHP files.
123 View Code Duplication
		if ( ! is_string( $file ) || '.php' !== substr( $file, -4 ) ) {
124
			return null;
125
		}
126
127
		$directories_to_check = self::normalize( $directories_to_check );
128
		foreach ( $directories_to_check as $check_dir ) {
0 ignored issues
show
Bug introduced by
The expression $directories_to_check of type string|array<integer,string> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
129
			$check = dirname( trailingslashit( $check_dir ) . $file );
130
			// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
131
			if ( @is_dir( $check ) ) {
132
				return $check;
133
			}
134
		}
135
136
		return null;
137
	}
138
139
	/**
140
	 * Normalizes the given path or paths so that it can be consistently
141
	 * handled throughout the autoloader.
142
	 *
143
	 * @param string|string[] $paths The path or paths we want to normalize.
144
	 * @return string|string[] The normalized path or paths.
145
	 */
146
	private static function normalize( $paths ) {
147
		// The paths will only be used in PHP and should have the same directory separators.
148
		return str_replace( '\\', '/', $paths );
149
	}
150
}
151