Skip to content

Commit fb7dce8

Browse files
committed
Only consider "Version" from php files with "Plugin Name"
1 parent f91c2dd commit fb7dce8

File tree

1 file changed

+58
-68
lines changed

1 file changed

+58
-68
lines changed

src/Version_Tool.php

Lines changed: 58 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,18 @@ public function get_version( string $path ): ?string {
2424

2525
if ( empty( $version ) ) {
2626
foreach ( glob( $path . '/*.php' ) as $php_file ) {
27-
$contents = file_get_contents( $php_file, false, null, 0, 5000 );
28-
$version = $this->get_version_in_code( $contents );
29-
if ( ! empty( $version ) ) {
30-
$version = trim( $version );
27+
$headers = $this->get_file_data(
28+
$php_file,
29+
array(
30+
'name' => 'Plugin Name',
31+
'version' => 'Version',
32+
)
33+
);
34+
if ( empty( $headers['name'] ) ) {
35+
continue;
36+
}
37+
if ( ! empty( $headers['version'] ) ) {
38+
$version = $headers['version'];
3139
break;
3240
}
3341
}
@@ -52,78 +60,60 @@ public function get_version( string $path ): ?string {
5260
}
5361

5462
/**
55-
* Gets the content of a version tag in any doc block in the given source code string.
63+
* Retrieves metadata from a file.
5664
*
57-
* The version tag might be specified as "@version x.y.z" or "Version: x.y.z" and it can
58-
* be preceded by an asterisk (*).
65+
* Modified slightly from WordPress 6.5.2 wp-includes/functions.php:6830
66+
* @see get_file_data()
67+
* @see https://github.com/WordPress/WordPress/blob/ddc3f387b5df4687f5b829119d0c0f797be674bf/wp-includes/functions.php#L6830-L6888
5968
*
60-
* @param string $code_str The source code string to look into.
61-
* @return null|string The detected version string.
62-
*/
63-
public function get_version_in_code( $code_str ) {
64-
$tokens = array_values(
65-
array_filter(
66-
token_get_all( $code_str ),
67-
function ( $token ) {
68-
return ! is_array( $token ) || T_WHITESPACE !== $token[0];
69-
}
70-
)
71-
);
72-
foreach ( $tokens as $token ) {
73-
if ( T_DOC_COMMENT === $token[0] ) {
74-
$version = $this->get_version_in_docblock( $token[1] );
75-
if ( null !== $version ) {
76-
return $version;
77-
}
78-
}
79-
}
80-
return null;
81-
}
82-
83-
/**
84-
* Gets the content of a version tag in a docblock.
69+
* Searches for metadata in the first 8 KB of a file, such as a plugin or theme.
70+
* Each piece of metadata must be on its own line. Fields can not span multiple
71+
* lines, the value will get cut at the end of the first line.
8572
*
86-
* @param string $docblock Docblock to parse.
87-
* @return null|string The content of the version tag.
73+
* @link https://codex.wordpress.org/File_Header
74+
*
75+
* @param string $file Absolute path to the file.
76+
* @param array $all_headers List of headers, in the format `array( 'HeaderKey' => 'Header Name' )`.
77+
* @return string[] Array of file header values keyed by header name.
8878
*/
89-
private function get_version_in_docblock( $docblock ) {
90-
$docblocktags = $this->parse_doc_block( $docblock );
91-
if ( isset( $docblocktags['version'] ) ) {
92-
return $docblocktags['version'];
79+
private function get_file_data( string $file, array $all_headers ): array {
80+
81+
/**
82+
* @see wp_initial_constants()
83+
* `define( 'KB_IN_BYTES', 1024 );`
84+
*/
85+
$kb_in_bytes = 1024;
86+
87+
// Pull only the first 8 KB of the file in.
88+
$file_data = file_get_contents( $file, false, null, 0, 8 * $kb_in_bytes );
89+
90+
if ( false === $file_data ) {
91+
$file_data = '';
9392
}
94-
return null;
95-
}
9693

97-
/**
98-
* Parses a docblock and gets an array of tags with their values.
99-
*
100-
* The tags might be specified as "@version x.y.z" or "Version: x.y.z" and they can
101-
* be preceded by an asterisk (*).
102-
*
103-
* This code is based on the 'phpactor' package.
104-
* @see https://github.com/phpactor/docblock/blob/master/lib/Parser.php
105-
*
106-
* @param string $docblock Docblock to parse.
107-
* @return array Associative array of parsed data.
108-
*/
109-
private function parse_doc_block( $docblock ) {
110-
$tag_documentor = '{@([a-zA-Z0-9-_\\\]+)\s*?(.*)?}';
111-
$tag_property = '{\s*\*?\s*(.*?):(.*)}';
112-
$lines = explode( PHP_EOL, $docblock );
113-
$tags = [];
114-
115-
foreach ( $lines as $line ) {
116-
if ( 0 === preg_match( $tag_documentor, $line, $matches ) ) {
117-
if ( 0 === preg_match( $tag_property, $line, $matches ) ) {
118-
continue;
119-
}
120-
}
94+
// Make sure we catch CR-only line endings.
95+
$file_data = str_replace( "\r", "\n", $file_data );
12196

122-
$tag_name = strtolower( $matches[1] );
123-
$metadata = trim( isset( $matches[2] ) ? $matches[2] : '' );
97+
/**
98+
* Strips close comment and close php tags from file headers used by WP.
99+
*
100+
* functions.php:6763
101+
*
102+
* @param string $str Header comment to clean up.
103+
* @return string
104+
*/
105+
$_cleanup_header_comment = function ( $str ) {
106+
return trim( preg_replace( '/\s*(?:\*\/|\?>).*/', '', $str ) );
107+
};
124108

125-
$tags[ $tag_name ] = $metadata;
109+
foreach ( $all_headers as $field => $regex ) {
110+
if ( preg_match( '/^(?:[ \t]*<\?php)?[ \t\/*#@]*' . preg_quote( $regex, '/' ) . ':(.*)$/mi', $file_data, $match ) && $match[1] ) {
111+
$all_headers[ $field ] = $_cleanup_header_comment( $match[1] );
112+
} else {
113+
$all_headers[ $field ] = '';
114+
}
126115
}
127-
return $tags;
116+
117+
return $all_headers;
128118
}
129119
}

0 commit comments

Comments
 (0)