Skip to content

PHP 8.2 | Ruleset: prevent notices about dynamic properties being set #3629

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,32 @@ http://pear.php.net/dtd/package-2.0.xsd">
</dir>
</dir>
<dir name="Ruleset">
<dir name="Fixtures">
<dir name="Sniffs">
<dir name="Category">
<file baseinstalldir="" name="SetPropertyAllowedAsDeclaredSniff.php" role="test" />
<file baseinstalldir="" name="SetPropertyAllowedViaMagicMethodSniff.php" role="test" />
<file baseinstalldir="" name="SetPropertyAllowedViaStdClassSniff.php" role="test" />
<file baseinstalldir="" name="SetPropertyNotAllowedViaAttributeSniff.php" role="test" />
</dir>
</dir>
</dir>
<file baseinstalldir="" name="RuleInclusionTest.php" role="test" />
<file baseinstalldir="" name="RuleInclusionTest.xml" role="test" />
<file baseinstalldir="" name="RuleInclusionTest-include.xml" role="test" />
<file baseinstalldir="" name="RuleInclusionAbsoluteLinuxTest.php" role="test" />
<file baseinstalldir="" name="RuleInclusionAbsoluteLinuxTest.xml" role="test" />
<file baseinstalldir="" name="RuleInclusionAbsoluteWindowsTest.php" role="test" />
<file baseinstalldir="" name="RuleInclusionAbsoluteWindowsTest.xml" role="test" />
<file baseinstalldir="" name="SetSniffPropertyTest.php" role="test" />
<file baseinstalldir="" name="SetPropertyAllowedAsDeclaredTest.xml" role="test" />
<file baseinstalldir="" name="SetPropertyAllowedViaMagicMethodTest.xml" role="test" />
<file baseinstalldir="" name="SetPropertyAllowedViaStdClassTest.xml" role="test" />
<file baseinstalldir="" name="SetPropertyAppliesPropertyToMultipleSniffsInCategoryTest.xml" role="test" />
<file baseinstalldir="" name="SetPropertyThrowsErrorOnInvalidPropertyTest.xml" role="test" />
<file baseinstalldir="" name="SetPropertyNotAllowedViaAttributeTest.xml" role="test" />
<file baseinstalldir="" name="SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForStandardTest.xml" role="test" />
<file baseinstalldir="" name="SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForCategoryTest.xml" role="test" />
</dir>
<dir name="Sniffs">
<file baseinstalldir="" name="AbstractArraySniffTest.inc" role="test" />
Expand Down Expand Up @@ -2128,6 +2147,19 @@ http://pear.php.net/dtd/package-2.0.xsd">
<install as="CodeSniffer/Core/Ruleset/RuleInclusionAbsoluteLinuxTest.xml" name="tests/Core/Ruleset/RuleInclusionAbsoluteLinuxTest.xml" />
<install as="CodeSniffer/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.php" name="tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.php" />
<install as="CodeSniffer/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.xml" name="tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetSniffPropertyTest.php" name="tests/Core/Ruleset/SetSniffPropertyTest.php" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyAllowedAsDeclaredTest.xml" name="tests/Core/Ruleset/SetPropertyAllowedAsDeclaredTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyAllowedViaMagicMethodTest.xml" name="tests/Core/Ruleset/SetPropertyAllowedViaMagicMethodTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyAllowedViaStdClassTest.xml" name="tests/Core/Ruleset/SetPropertyAllowedViaStdClassTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyAppliesPropertyToMultipleSniffsInCategoryTest.xml" name="tests/Core/Ruleset/SetPropertyAppliesPropertyToMultipleSniffsInCategoryTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyNotAllowedViaAttributeTest.xml" name="tests/Core/Ruleset/SetPropertyNotAllowedViaAttributeTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForCategoryTest.xml" name="tests/Core/Ruleset/SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForCategoryTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForStandardTest.xml" name="tests/Core/Ruleset/SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForStandardTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyThrowsErrorOnInvalidPropertyTest.xml" name="tests/Core/Ruleset/SetPropertyThrowsErrorOnInvalidPropertyTest.xml" />
<install as="CodeSniffer/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyAllowedAsDeclaredSniff.php" name="tests/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyAllowedAsDeclaredSniff.php" />
<install as="CodeSniffer/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyAllowedViaMagicMethodSniff.php" name="tests/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyAllowedViaMagicMethodSniff.php" />
<install as="CodeSniffer/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyAllowedViaStdClassSniff.php" name="tests/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyAllowedViaStdClassSniff.php" />
<install as="CodeSniffer/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyNotAllowedViaAttributeSniff.php" name="tests/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyNotAllowedViaAttributeSniff.php" />
<install as="CodeSniffer/Core/Sniffs/AbstractArraySniffTest.inc" name="tests/Core/Sniffs/AbstractArraySniffTest.inc" />
<install as="CodeSniffer/Core/Sniffs/AbstractArraySniffTest.php" name="tests/Core/Sniffs/AbstractArraySniffTest.php" />
<install as="CodeSniffer/Core/Sniffs/AbstractArraySniffTestable.php" name="tests/Core/Sniffs/AbstractArraySniffTestable.php" />
Expand Down Expand Up @@ -2236,6 +2268,19 @@ http://pear.php.net/dtd/package-2.0.xsd">
<install as="CodeSniffer/Core/Ruleset/RuleInclusionAbsoluteLinuxTest.xml" name="tests/Core/Ruleset/RuleInclusionAbsoluteLinuxTest.xml" />
<install as="CodeSniffer/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.php" name="tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.php" />
<install as="CodeSniffer/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.xml" name="tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetSniffPropertyTest.php" name="tests/Core/Ruleset/SetSniffPropertyTest.php" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyAllowedAsDeclaredTest.xml" name="tests/Core/Ruleset/SetPropertyAllowedAsDeclaredTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyAllowedViaMagicMethodTest.xml" name="tests/Core/Ruleset/SetPropertyAllowedViaMagicMethodTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyAllowedViaStdClassTest.xml" name="tests/Core/Ruleset/SetPropertyAllowedViaStdClassTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyNotAllowedViaAttributeTest.xml" name="tests/Core/Ruleset/SetPropertyNotAllowedViaAttributeTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyAppliesPropertyToMultipleSniffsInCategoryTest.xml" name="tests/Core/Ruleset/SetPropertyAppliesPropertyToMultipleSniffsInCategoryTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForCategoryTest.xml" name="tests/Core/Ruleset/SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForCategoryTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForStandardTest.xml" name="tests/Core/Ruleset/SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForStandardTest.xml" />
<install as="CodeSniffer/Core/Ruleset/SetPropertyThrowsErrorOnInvalidPropertyTest.xml" name="tests/Core/Ruleset/SetPropertyThrowsErrorOnInvalidPropertyTest.xml" />
<install as="CodeSniffer/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyAllowedAsDeclaredSniff.php" name="tests/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyAllowedAsDeclaredSniff.php" />
<install as="CodeSniffer/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyAllowedViaMagicMethodSniff.php" name="tests/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyAllowedViaMagicMethodSniff.php" />
<install as="CodeSniffer/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyAllowedViaStdClassSniff.php" name="tests/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyAllowedViaStdClassSniff.php" />
<install as="CodeSniffer/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyNotAllowedViaAttributeSniff.php" name="tests/Core/Ruleset/Fixtures/Sniffs/Category/SetPropertyNotAllowedViaAttributeSniff.php" />
<install as="CodeSniffer/Core/Sniffs/AbstractArraySniffTest.inc" name="tests/Core/Sniffs/AbstractArraySniffTest.inc" />
<install as="CodeSniffer/Core/Sniffs/AbstractArraySniffTest.php" name="tests/Core/Sniffs/AbstractArraySniffTest.php" />
<install as="CodeSniffer/Core/Sniffs/AbstractArraySniffTestable.php" name="tests/Core/Sniffs/AbstractArraySniffTestable.php" />
Expand Down
1 change: 1 addition & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

<exclude-pattern>*/src/Standards/*/Tests/*\.(inc|css|js)$</exclude-pattern>
<exclude-pattern>*/tests/Core/*/*\.(inc|css|js)$</exclude-pattern>
<exclude-pattern>*/tests/Core/*/Fixtures/*\.php$</exclude-pattern>

<arg name="basepath" value="."/>
<arg name="colors"/>
Expand Down
5 changes: 3 additions & 2 deletions scripts/ValidatePEAR/ValidatePEARPackageXML.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,11 @@ protected function checkFileTag($tag, $currentDirectory='')
$valid = false;
} else {
// Limited validation of the "role" tags.
if (strpos($name, 'Test.') !== false && $role !== 'test') {
if ((strpos($name, 'tests/') === 0 || strpos($name, 'Test.') !== false) && $role !== 'test') {
echo "- Test files should have the role 'test'. Found: '$role' for file '{$name}'.".PHP_EOL;
$valid = false;
} else if ((strpos($name, 'Standard.xml') !== false || strpos($name, 'Sniff.php') !== false)
} else if (strpos($name, 'tests/') !== 0
&& (strpos($name, 'Standard.xml') !== false || strpos($name, 'Sniff.php') !== false)
&& $role !== 'php'
) {
echo "- Sniff files, including sniff documentation files should have the role 'php'. Found: '$role' for file '{$name}'.".PHP_EOL;
Expand Down
14 changes: 10 additions & 4 deletions src/Files/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,12 @@ public function process()
if (isset($this->ruleset->sniffCodes[$parts[0]]) === true) {
$listenerCode = array_shift($parts);
$propertyCode = array_shift($parts);
$propertyValue = rtrim(implode(' ', $parts), " */\r\n");
$settings = [
'value' => rtrim(implode(' ', $parts), " */\r\n"),
'scope' => 'sniff',
];
$listenerClass = $this->ruleset->sniffCodes[$listenerCode];
$this->ruleset->setSniffProperty($listenerClass, $propertyCode, $propertyValue);
$this->ruleset->setSniffProperty($listenerClass, $propertyCode, $settings);
}
}
}
Expand All @@ -403,9 +406,12 @@ public function process()
$listenerCode = $token['sniffCode'];
if (isset($this->ruleset->sniffCodes[$listenerCode]) === true) {
$propertyCode = $token['sniffProperty'];
$propertyValue = $token['sniffPropertyValue'];
$settings = [
'value' => $token['sniffPropertyValue'],
'scope' => 'sniff',
];
$listenerClass = $this->ruleset->sniffCodes[$listenerCode];
$this->ruleset->setSniffProperty($listenerClass, $propertyCode, $propertyValue);
$this->ruleset->setSniffProperty($listenerClass, $propertyCode, $settings);
}
}
}//end if
Expand Down
92 changes: 81 additions & 11 deletions src/Ruleset.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use PHP_CodeSniffer\Exceptions\RuntimeException;
use PHP_CodeSniffer\Util;
use stdClass;

class Ruleset
{
Expand Down Expand Up @@ -960,6 +961,11 @@ private function processRule($rule, $newSniffs, $depth=0)
if (isset($rule->properties) === true
&& $this->shouldProcessElement($rule->properties) === true
) {
$propertyScope = 'standard';
if ($code === $ref || substr($ref, -9) === 'Sniff.php') {
$propertyScope = 'sniff';
}

foreach ($rule->properties->property as $prop) {
if ($this->shouldProcessElement($prop) === false) {
continue;
Expand All @@ -980,9 +986,9 @@ private function processRule($rule, $newSniffs, $depth=0)
$values = [];
if (isset($prop['extend']) === true
&& (string) $prop['extend'] === 'true'
&& isset($this->ruleset[$code]['properties'][$name]) === true
&& isset($this->ruleset[$code]['properties'][$name]['value']) === true
) {
$values = $this->ruleset[$code]['properties'][$name];
$values = $this->ruleset[$code]['properties'][$name]['value'];
}

if (isset($prop->element) === true) {
Expand Down Expand Up @@ -1017,7 +1023,10 @@ private function processRule($rule, $newSniffs, $depth=0)
}
}//end if

$this->ruleset[$code]['properties'][$name] = $values;
$this->ruleset[$code]['properties'][$name] = [
'value' => $values,
'scope' => $propertyScope,
];
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo str_repeat("\t", $depth);
echo "\t\t=> array property \"$name\" set to \"$printValue\"";
Expand All @@ -1028,7 +1037,10 @@ private function processRule($rule, $newSniffs, $depth=0)
echo PHP_EOL;
}
} else {
$this->ruleset[$code]['properties'][$name] = (string) $prop['value'];
$this->ruleset[$code]['properties'][$name] = [
'value' => (string) $prop['value'],
'scope' => $propertyScope,
];
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo str_repeat("\t", $depth);
echo "\t\t=> property \"$name\" set to \"".(string) $prop['value'].'"';
Expand Down Expand Up @@ -1218,8 +1230,8 @@ public function populateTokenListeners()

// Set custom properties.
if (isset($this->ruleset[$sniffCode]['properties']) === true) {
foreach ($this->ruleset[$sniffCode]['properties'] as $name => $value) {
$this->setSniffProperty($sniffClass, $name, $value);
foreach ($this->ruleset[$sniffCode]['properties'] as $name => $settings) {
$this->setSniffProperty($sniffClass, $name, $settings);
}
}

Expand Down Expand Up @@ -1286,18 +1298,76 @@ public function populateTokenListeners()
*
* @param string $sniffClass The class name of the sniff.
* @param string $name The name of the property to change.
* @param string $value The new value of the property.
* @param array $settings Array with the new value of the property and the scope of the property being set.
*
* @return void
*
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException When attempting to set a non-existent property on a sniff
* which doesn't declare the property or explicitly supports
* dynamic properties.
*/
public function setSniffProperty($sniffClass, $name, $value)
public function setSniffProperty($sniffClass, $name, $settings)
{
// Setting a property for a sniff we are not using.
if (isset($this->sniffs[$sniffClass]) === false) {
return;
}

$name = trim($name);
$name = trim($name);
$propertyName = $name;
if (substr($propertyName, -2) === '[]') {
$propertyName = substr($propertyName, 0, -2);
}

/*
* BC-compatibility layer for $settings using the pre-PHPCS 3.8.0 format.
*
* Prior to PHPCS 3.8.0, `$settings` was expected to only contain the new _value_
* for the property (which could be an array).
* Since PHPCS 3.8.0, `$settings` is expected to be an array with two keys: 'scope'
* and 'value', where 'scope' indicates whether the property should be set to the given 'value'
* for one individual sniff or for all sniffs in a standard.
*
* This BC-layer is only for integrations with PHPCS which may call this method directly
* and will be removed in PHPCS 4.0.0.
*/

if (is_array($settings) === false
|| isset($settings['scope'], $settings['value']) === false
) {
// This will be an "old" format value.
$settings = [
'value' => $settings,
'scope' => 'standard',
];

trigger_error(
__FUNCTION__.': the format of the $settings parameter has changed from (mixed) $value to array(\'scope\' => \'sniff|standard\', \'value\' => $value). Please update your integration code. See PR #3629 for more information.',
E_USER_DEPRECATED
);
}

$isSettable = false;
$sniffObject = $this->sniffs[$sniffClass];
if (property_exists($sniffObject, $propertyName) === true
|| ($sniffObject instanceof stdClass) === true
|| method_exists($sniffObject, '__set') === true
) {
$isSettable = true;
}

if ($isSettable === false) {
if ($settings['scope'] === 'sniff') {
$notice = "Ruleset invalid. Property \"$propertyName\" does not exist on sniff ";
$notice .= array_search($sniffClass, $this->sniffCodes, true);
throw new RuntimeException($notice);
}

return;
}

$value = $settings['value'];

if (is_string($value) === true) {
$value = trim($value);
}
Expand All @@ -1312,7 +1382,7 @@ public function setSniffProperty($sniffClass, $name, $value)
} else if ($value === 'false') {
$value = false;
} else if (substr($name, -2) === '[]') {
$name = substr($name, 0, -2);
$name = $propertyName;
$values = [];
if ($value !== null) {
foreach (explode(',', $value) as $val) {
Expand All @@ -1328,7 +1398,7 @@ public function setSniffProperty($sniffClass, $name, $value)
$value = $values;
}

$this->sniffs[$sniffClass]->$name = $value;
$sniffObject->$name = $value;

}//end setSniffProperty()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
/**
* Test fixture.
*
* @see \PHP_CodeSniffer\Tests\Core\Ruleset\SetSniffPropertyTest
*/

namespace Fixtures\Sniffs\Category;

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;

class SetPropertyAllowedAsDeclaredSniff implements Sniff
{

public $arbitrarystring;
public $arbitraryarray;

public function register()
{
return [T_WHITESPACE];
}

public function process(File $phpcsFile, $stackPtr)
{
// Do something.
}
}
Loading