Skip to content

Commit b8c5063

Browse files
committed
Generate hook names using parsed Node instead of pretty-printed name.
Previously, the hook-name printer was reyling on the parser's pretty printer to generate names, and then post-processing the name to extract string literal values and concatenations. Unfortunately this left some cases unhandled, specifically the case where concatenations are joining function calls with string literals. Any number of other unexpected situations might arise, and there can be defects in the redundant code attempting to parse PHP syntax. In this patch the pretty-printed version of the expression is used only as a fallback in unrecognized situations. Primarily, a direct encoding from the parsed syntax tree to string is used to rely on the parser's own handling of syntax, and making it clearer how to add additional support for other syntaxes.
1 parent 7fc2227 commit b8c5063

File tree

1 file changed

+52
-26
lines changed

1 file changed

+52
-26
lines changed

lib/class-hook-reflector.php

+52-26
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,66 @@
1111
class Hook_Reflector extends BaseReflector {
1212

1313
/**
14-
* @return string
15-
*/
16-
public function getName() {
17-
$printer = new PHPParser_PrettyPrinter_Default;
18-
return $this->cleanupName( $printer->prettyPrintExpr( $this->node->args[0]->value ) );
19-
}
20-
21-
/**
22-
* @param string $name
14+
* Hook names are the first argument to actions and filters.
15+
*
16+
* These are expected to be string values or concatenations of strings and variables.
2317
*
18+
* Example:
19+
*
20+
* apply_filters( 'option_' . $option_name, $option_value );
21+
* do_action( 'wp_insert_post', $post_ID, $post, true );
22+
* do_action( "{$old_status}_to_{$new_status}", $post );
23+
* do_action( $filter_name, $args );
24+
*
25+
* @param ?\PhpParser\Node $node Use this instead of the current node.
2426
* @return string
2527
*/
26-
private function cleanupName( $name ) {
27-
$matches = array();
28-
29-
// quotes on both ends of a string
30-
if ( preg_match( '/^[\'"]([^\'"]*)[\'"]$/', $name, $matches ) ) {
31-
return $matches[1];
32-
}
28+
public function getName( $node = null ) {
29+
/**
30+
* The current Node being examined.
31+
*
32+
* @var \PhpParser\Node
33+
*/
34+
$node = $node ?? $this->node->args[0]->value;
35+
$printer = new PHPParser_PrettyPrinter_Default;
36+
$name = $printer->prettyPrintExpr( $node );
3337

34-
// two concatenated things, last one of them a variable
35-
if ( preg_match(
36-
'/(?:[\'"]([^\'"]*)[\'"]\s*\.\s*)?' . // First filter name string (optional)
37-
'(\$[^\s]*)' . // Dynamic variable
38-
'(?:\s*\.\s*[\'"]([^\'"]*)[\'"])?/', // Second filter name string (optional)
39-
$name, $matches ) ) {
38+
if ( $node instanceof \PhpParser\Node\Scalar\String_ ) {
39+
// "'action'" -> "action"
40+
return $node->value;
41+
} elseif ( $node instanceof \PhpParser\Node\Scalar\Encapsed ) {
42+
// '"action_{$var}"' -> 'action_{$var}'
43+
$name = '';
4044

41-
if ( isset( $matches[3] ) ) {
42-
return $matches[1] . '{' . $matches[2] . '}' . $matches[3];
43-
} else {
44-
return $matches[1] . '{' . $matches[2] . '}';
45+
foreach ( $node->parts as $part ) {
46+
if ( is_string( $part ) ) {
47+
$name .= $part;
48+
} else {
49+
$name .= $this->getName( $part );
50+
}
4551
}
52+
53+
return $name;
54+
} elseif ( $node instanceof \PhpParser\Node\Expr\BinaryOp\Concat ) {
55+
// '"action_" . $var' -> 'action_{$var}'
56+
return $this->getName( $node->left ) . $this->getName( $node->right );
57+
} elseif ( $node instanceof \PhpParser\Node\Expr\PropertyFetch ) {
58+
// '$this->action' -> '{$this->action}'
59+
return "{{$name}}";
60+
} elseif ( $node instanceof \PhpParser\Node\Expr\Variable ) {
61+
// '$action' -> '{$action}'
62+
return "{\${$node->name}}";
4663
}
4764

65+
/*
66+
* If none of these known constructions match, then
67+
* fallback to the pretty-printed version of the node.
68+
*
69+
* For improving the quality of the hook-name generation,
70+
* replace this return statement by throwing an exception
71+
* to determine which cases aren't handled, and then add
72+
* them above.
73+
*/
4874
return $name;
4975
}
5076

0 commit comments

Comments
 (0)