-
-
Notifications
You must be signed in to change notification settings - Fork 493
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding missing escaping check for <?=
#858
Adding missing escaping check for <?=
#858
Conversation
This commit registers `T_OPEN_TAG_WITH_ECHO` in EscapeOutputSniff and adds new test for those cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@david-binda Looks good to me! Thanks.
FYI: I ran some additional tests with some slightly more complicated code within the short open tags and the sniff seems to hold its own.
<?= ( isset( $something ) ? $something : '' ?>
, function calls within the tags etc.
Travis CI may not be responding for awhile due to the AWS outage. |
<?php | ||
|
||
// Ok. | ||
<?= esc_html( $var ); ?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a PHP closing tag is missing here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seeing the build failure: you could just change the comment to an HTML comment instead ?
<?php | ||
|
||
// Ok. | ||
<?= esc_html( $var ); ?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. The immediately-preceding <?php
can be removed.
Since this replacement changed the line numbering, this commit also updates the line number for related test in the *UnitTest.php file
@david-binda Just checking: do you know how to fix the PHP 5.3 build failure or do you need a hand with that ? |
@jrfnl thanks for checking! I'll need to fire up some 5.3 environment and run the phpunit tests in there. My guess is that the shorttag is not being properly parsed in that version. I'll figure that out. |
@david-binda I suspect you may need to look at the |
@jrfnl right. It actually is the culprit. I'm looking into whether there might be some way to report on the short open tags even when that |
@david-binda You may want to have a look here for inspiration: https://github.com/squizlabs/PHP_CodeSniffer/blob/master/CodeSniffer/Standards/Generic/Sniffs/PHP/DisallowAlternativePHPTagsSniff.php As for the unit tests, you can provide an ini settings file to travis to use. See here for an example: https://github.com/squizlabs/PHP_CodeSniffer/blob/master/.travis.yml |
Thanks, that's helpful. I have something similar in place.
@jrfnl I don't think that the failing test is what should be fixed. Or at least not the only thing which should be fixed. The sniff, imho, should definitely report all |
Very true, but both situations -
I'd need to see some example code before I can give an opinion on the approach, but in case you decide to go with regex and need help with that, you're welcome to ping me. |
…rt_open_tag in case they are disabled in PHP In case the string is not obvisouly unespcaed, Warning is thrown instead asking for manual inspection. This commit also adjusts the tests accordingly - conditionally adds Warning on line 211 for case short_open_tag is disabled
…ith short_open_tag set to On (it's off by default)
Whoops. Looks like I have broken the Sniff outside the PHP 5.3. I'll look into it. |
); | ||
|
||
// We can successfully report on unescaped open_short_tags only if they are enabled. | ||
if ( true === (bool) ini_get( 'short_open_tag' ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@david-binda This is the issue for PHP 5.4+ as the T_OPEN_TAG_WITH_ECHO
is supported independently of the short_open_tag
setting from PHP 5.4+ upwards, so basically, leave the token in the base array and only use the else
clause of this part with the reversed condition and you should be fine.
See changelog here: http://php.net/manual/en/language.basic-syntax.phptags.php
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Build is only failing on some small code style issues now, so we're nearly there.
* which is how open_short_tags are being handled in that case. | ||
*/ | ||
if ( false === $this->is_short_open_tag_enabled() ) { | ||
var_export( 'Short open tag is disabled!' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove the debug code.
* | ||
* @return bool False if short_open_tag is not enabled, true otherwise | ||
*/ | ||
public function is_short_open_tag_enabled() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The returned value of this function is not going to change during the PHPCS run, so I'd like to suggest adding a class (private) property instead, assigning the value to the property at the start of the register()
function and referring to the property later on for efficiency.
* @return bool False if short_open_tag is not enabled, true otherwise | ||
*/ | ||
public function is_short_open_tag_enabled() { | ||
if ( true === version_compare(phpversion(), '5.4', '<' ) && false === (bool) ini_get( 'short_open_tag' ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use the PHP_VERSION_ID
constant instead for the comparison (which is backfilled in PHPCS).
PHP_VERSION_ID < 50400
is two less functions calls for the same result.
@@ -77,7 +77,7 @@ public function getWarningList() { | |||
$list = array(); | |||
|
|||
//Adding Warning which is triggerred in case open_short_tag is set to Off | |||
if ( false === (bool) ini_get( 'short_open_tag' ) ) { | |||
if ( true === version_compare(phpversion(), '5.4', '<' ) && false === (bool) ini_get( 'short_open_tag' ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use the PHP_VERSION_ID
constant instead (see above)
* which is how open_short_tags are being handled in that case. | ||
*/ | ||
/** | ||
* In case open_short_tag are turned off, we can attempt to regex T_INLINE_HTML |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are
=> is
*/ | ||
/** | ||
* In case open_short_tag are turned off, we can attempt to regex T_INLINE_HTML | ||
* which is how open_short_tags are being handled in that case. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
open_short_tags
=> short open tags
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two more small remarks
} | ||
|
||
// Throw warning in case the T_INLINE_HTML looks like a open_short_tag | ||
if ( false !== strpos( $this->tokens[$stackPtr]['content'], '<?=' ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a separate sniff for this, so no need to throw that warning here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jrfnl what Sniff do you have in mind, please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both short open tags as well as alternative open tags are sniffed for in the WordPress-Core
ruleset:
https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/blob/develop/WordPress-Core/ruleset.xml#L104-L105
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jrfnl I'm happy to get rid of his Warning, but I have noticed that in PHP 5.3 with disabled short_open_tag the referenced sniff is not producing any warnings (as it's hitting the same issue with T_OPEN_TAG_WITH_ECHO
as we are ).
So as long as we already have something in place for checking on T_INLINE_HTML
, it might be good to take an advantage of that and report on those as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just checked and you're right. I thought I'd pulled that upstream, but seems I only included that logic when I pulled the Generic.PHP.DisallowAlternativePHPTags
sniff.
So, yes. let's leave the warning in place for now until the upstream sniff has been improved.
You may want to open an issue for this upstream.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Upstream issue opened: squizlabs/PHP_CodeSniffer#1398
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've opened an upstream PR to fix the Generic.PHP.DisallowAlternativePHPTags
sniff squizlabs/PHP_CodeSniffer#1400
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh and before I forget: the upstream PR was merged and will be included in the next version of PHPCS, so the additional message can be removed once we up the minimum PHPCS version.
} | ||
|
||
// Report on what very likely is a PHP short open tag outputing variable | ||
if ( preg_match( '/\<\?\=[\s\t]*(\$[a-zA-Z_][[:alnum:]_]+)[\s\t]*\;?[\s\t]*\?\>/', $this->tokens[ $stackPtr ]['content'], $matches ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- The regex for a variable name in PHP is:
[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
Ref: http://php.net/manual/en/language.variables.basics.php \t
is already included in\s
, so no need to make it explicit in the character class.;
does not need to be escaped in regex
Looks like the regex does not detect the echo-ing out of array based values, i.e. $var['something']
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good call on the array based values. Outputting object properties might suffer from the same issue. I'll look into those cases later in the day. Thanks for pointing that out!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my suggestion below
Since the returned value is not going to be changed during the PHPCS run, we can store the value in a private proprty in order to improve sniff's efficiency
…VERSION_ID check since it being backfilled in PHPCS and thus can be used for our needs.
…bled * Using the variable regex mentioned in the PHP manual: http://php.net/manual/en/language.variables.basics.php * Not specifically mentioning `\t` as it's already included in `\s` * Not escaping `;` as it's not necessary The regex still needs to be improved in order to catch array based values and possibly object based values.
… in the first round :-/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for being a pain in the butt...
/** | ||
* In case open_short_tag is turned off, we can attempt to regex T_INLINE_HTML | ||
* which is how open short tags are being handled in that case. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
open_short_tag
=> short_open_tag
open short tags
=> short open tags
* | ||
* @var bool | ||
*/ | ||
private $short_open_tag_enabled = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a @since
tag
* Returns an array of tokens this test wants to listen for. | ||
* | ||
* @return array | ||
*/ | ||
public function register() { | ||
// Check whether short_open_tag is disabled on PHP version < 5.4 (it''s enabled by default in later versions). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it''s
=> it's
} | ||
|
||
// Report on what very likely is a PHP short open tag outputing variable. | ||
if ( preg_match( '/\<\?\=[\s]*(\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\s]*;?[\s]*\?\>/', $this->tokens[ $stackPtr ]['content'], $matches ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re: your remark that the regex still needs improving for array and object based variables:
What about changing the regex to:
/\<\?\=[\s]*(\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:(?:->\S+|\[[^\]]+\]))*)[\s]*;?[\s]*\?\>/
); | ||
|
||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't a docblock, so it should only have one *
.
} | ||
|
||
// Report on what very likely is a PHP short open tag outputing variable. | ||
if ( preg_match( '/\<\?\=[\s]*(\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\s]*;?[\s]*\?\>/', $this->tokens[ $stackPtr ]['content'], $matches ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just thinking out loud, I wonder if there is a way to replace each <?=
with <?php echo
and then reparse that part of the string as PHP. That would avoid regex entirely. But perhaps it really isn't such a good idea, or isn't really worth it even if it is possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is definitely possible, but might be more complicated and I wonder if it's really worth it as it would only be necessary for PHP <= 5.3 with short_open_tag
off.
What would make it more complicated is that the T_INLINE_HTML
token is split at line-end and you may need to join several T_INLINE_HTML tokens together if the php code is split over several lines as otherwise the tokenizer might fail.
Think:
<p>some text<?=
$variable;
?></p>
N.B.: the php code split over several T_INLINE_HTML tokens issue is not being addressed with the regex either, but as it's an edge-case, I don't have a problem with leaving that as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it sounds like it isn't worth it.
…rray and object based variables
I'll look into the failing regex, tho |
Gah, turned out to be a bad copy-pasting 😊 |
return; | ||
} | ||
|
||
// Report on what very likely is a PHP short open tag outputing variable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Darn.. missed this before: outputing
=> outputting
} | ||
|
||
// Throw warning in case the T_INLINE_HTML looks like a open_short_tag | ||
if ( false !== strpos( $this->tokens[$stackPtr]['content'], '<?=' ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just checked and you're right. I thought I'd pulled that upstream, but seems I only included that logic when I pulled the Generic.PHP.DisallowAlternativePHPTags
sniff.
So, yes. let's leave the warning in place for now until the upstream sniff has been improved.
You may want to open an issue for this upstream.
@david-binda Thanks for all your efforts on this. This PR can now be merged. As we're about to release 0.11.0, however, I suggest waiting to merge this PR until after the release so it can be thoroughly tested before being included in the next release. |
+1 Great work @david-binda. |
@david-binda Could you please rebase the PR on the current |
@@ -152,6 +175,24 @@ public function process_token( $stackPtr ) { | |||
if ( in_array( $function, array( 'trigger_error', 'user_error' ), true ) ) { | |||
$end_of_statement = $this->phpcsFile->findEndOfStatement( $open_paren + 1 ); | |||
} | |||
} else if ( false === $this->short_open_tag_enabled && T_INLINE_HTML === $this->tokens[ $stackPtr ]['code'] ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just noticed one last minor thingie: the first part of this if
condition is not necessary as T_INLINE_HTML
is only registered to this sniff if short_open_tag_enabled
is true
.
Having said that, that also means that the property can be removed and turned into a local variable within the register()
method.
Hi @david-binda, just wondering if you saw the last few messages. Would be great if you could rebase the PR and fix the tiny last note. I look forward to having this in WPCS 😻 |
Wanted to see if I could help in any way to push this through. |
PR #1010 supersedes this PR and takes care of the issues which were still open. |
This commit registers
T_OPEN_TAG_WITH_ECHO
in EscapeOutputSniff and adds new test for those casesFixes #857
cc @westonruter