Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
19 changes: 15 additions & 4 deletions CakePHP/Sniffs/NamingConventions/ValidFunctionNameSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ class ValidFunctionNameSniff extends AbstractScopeSniff
/**
* A list of all PHP magic methods.
*
* @var array
* @var array<string>
*/
protected array $_magicMethods = [
protected array $magicMethods = [
'construct',
'destruct',
'call',
Expand All @@ -54,7 +54,7 @@ class ValidFunctionNameSniff extends AbstractScopeSniff
*/
public function __construct()
{
parent::__construct([T_CLASS, T_INTERFACE, T_TRAIT], [T_FUNCTION], true);
parent::__construct([T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM], [T_FUNCTION], true);
}

/**
Expand All @@ -71,7 +71,7 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
$errorData = [$className . '::' . $methodName];

// Ignore magic methods
if (preg_match('/^__(' . implode('|', $this->_magicMethods) . ')$/', $methodName)) {
if (preg_match('/^__(' . implode('|', $this->magicMethods) . ')$/', $methodName)) {
return;
}

Expand All @@ -89,6 +89,17 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop

return;
}

// Check non-public methods for underscore prefix
if ($isPublic === false && $methodName[0] === '_') {
// Allow CakePHP Entity accessor/mutator pattern: _getField(), _setField()
if (preg_match('/^_(get|set)[A-Z]/', $methodName)) {
return;
}

$error = 'Non-public method name "%s" should not be prefixed with underscore';
$phpcsFile->addError($error, $stackPtr, 'ProtectedWithUnderscore', $errorData);
}
}

/**
Expand Down
69 changes: 69 additions & 0 deletions CakePHP/Sniffs/NamingConventions/ValidPropertyNameSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://github.com/cakephp/cakephp-codesniffer
* @since CakePHP CodeSniffer 6.0.0
* @license https://www.opensource.org/licenses/mit-license.php MIT License
*/

/**
* Ensures property names follow PSR naming conventions.
Comment thread
dereuromark marked this conversation as resolved.
Outdated
*
* Non-public properties should not be prefixed with an underscore.
*/
namespace CakePHP\Sniffs\NamingConventions;

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\AbstractVariableSniff;

class ValidPropertyNameSniff extends AbstractVariableSniff
{
/**
* @inheritDoc
*/
protected function processMemberVar(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$propName = ltrim($tokens[$stackPtr]['content'], '$');

// Only check properties starting with underscore
if ($propName[0] !== '_') {
return;
}

$props = $phpcsFile->getMemberProperties($stackPtr);

// Public properties with underscore are also bad, but less common
// Focus on protected/private which was the old convention
if ($props['scope'] !== 'public') {
$error = 'Non-public property "$%s" should not be prefixed with underscore';
$phpcsFile->addError($error, $stackPtr, 'PropertyWithUnderscore', [$propName]);
} else {
$error = 'Public property "$%s" must not be prefixed with underscore';
$phpcsFile->addError($error, $stackPtr, 'PublicPropertyWithUnderscore', [$propName]);
}
}

/**
* @inheritDoc
*/
protected function processVariable(File $phpcsFile, $stackPtr)
{
// We only care about member variables (properties)
}

/**
* @inheritDoc
*/
protected function processVariableInString(File $phpcsFile, $stackPtr)
{
// We only care about member variables (properties)
}
}
18 changes: 17 additions & 1 deletion CakePHP/Tests/NamingConventions/ValidFunctionNameUnitTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class FunctionNames

protected function _someFunc()
{
// code here
// code here - error: underscore prefix not allowed
}

protected function noUnderscorePrefix()
Expand All @@ -40,6 +40,22 @@ class FunctionNames
};
}

// Entity accessor/mutator patterns - should be allowed
protected function _getName()
{
return $this->name;
}

protected function _setName($name)
{
$this->name = $name;
}

protected function _getFullName()
{
return $this->first_name . ' ' . $this->last_name;
}

public function __call($name, $arguments)
{
}
Expand Down
12 changes: 7 additions & 5 deletions CakePHP/Tests/NamingConventions/ValidFunctionNameUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@
/**
* @inheritDoc
*/
public function getErrorList()
public function getErrorList(): array
{
return [
6 => 1,
87 => 1,
96 => 1,
6 => 1, // public function _forbidden

Check failure on line 15 in CakePHP/Tests/NamingConventions/ValidFunctionNameUnitTest.php

View workflow job for this annotation

GitHub Actions / Coding Standard & Static Analysis

Double space found
30 => 1, // protected function _someFunc

Check failure on line 16 in CakePHP/Tests/NamingConventions/ValidFunctionNameUnitTest.php

View workflow job for this annotation

GitHub Actions / Coding Standard & Static Analysis

Double space found
103 => 1, // public function _forbidden (interface)
112 => 1, // public function _forbidden (trait)
136 => 1, // protected function _someFunc (trait)
];
}

/**
* @inheritDoc
*/
public function getWarningList()
public function getWarningList(): array
{
return [];
}
Expand Down
39 changes: 39 additions & 0 deletions CakePHP/Tests/NamingConventions/ValidPropertyNameUnitTest.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Beakman;

class PropertyNames
{
public string $publicProperty = '';

public string $_publicUnderscore = ''; // error

protected string $protectedProperty = '';

protected string $_protectedUnderscore = ''; // error

private string $privateProperty = '';

private string $_privateUnderscore = ''; // error

public static string $publicStatic = '';

protected static string $_protectedStatic = ''; // error

private static string $_privateStatic = ''; // error
}

trait PropertyNamesTrait
{
public string $publicProperty = '';

public string $_publicUnderscore = ''; // error

protected string $protectedProperty = '';

protected string $_protectedUnderscore = ''; // error

private string $privateProperty = '';

private string $_privateUnderscore = ''; // error
}
33 changes: 33 additions & 0 deletions CakePHP/Tests/NamingConventions/ValidPropertyNameUnitTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace CakePHP\Tests\NamingConventions;

use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;

class ValidPropertyNameUnitTest extends AbstractSniffTestCase
{
/**
* @inheritDoc
*/
public function getErrorList(): array
{
return [
9 => 1, // public $_publicUnderscore

Check failure on line 15 in CakePHP/Tests/NamingConventions/ValidPropertyNameUnitTest.php

View workflow job for this annotation

GitHub Actions / Coding Standard & Static Analysis

Double space found
13 => 1, // protected $_protectedUnderscore
17 => 1, // private $_privateUnderscore
21 => 1, // protected static $_protectedStatic
23 => 1, // private static $_privateStatic
30 => 1, // public $_publicUnderscore (trait)
34 => 1, // protected $_protectedUnderscore (trait)
38 => 1, // private $_privateUnderscore (trait)
];
}

/**
* @inheritDoc
*/
public function getWarningList(): array
{
return [];
}
}
9 changes: 6 additions & 3 deletions CakePHP/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@
<exclude name="PSR12.Files.FileHeader.SpacingAfterUseBlock"/>
<exclude name="PSR12.Files.FileHeader.SpacingAfterUseFunctionBlock"/>
<!--
Property and method names with underscore prefix are allowed in CakePHP.
Not using underscore prefix is a recommendation of PSR2, not a requirement.
We use our own CakePHP.NamingConventions.ValidFunctionName sniff which allows
Entity accessor/mutator pattern (_getField, _setField) while enforcing PSR naming elsewhere.
-->
<exclude name="PSR2.Classes.PropertyDeclaration.Underscore"/>
<exclude name="PSR2.Methods.MethodDeclaration.Underscore"/>
</rule>

Expand Down Expand Up @@ -256,6 +255,10 @@
<rule ref="SlevomatCodingStandard.Attributes.RequireAttributeAfterDocComment"/>
<rule ref="SlevomatCodingStandard.Classes.BackedEnumTypeSpacing"/>
<rule ref="SlevomatCodingStandard.ControlStructures.RequireShortTernaryOperator"/>
<rule ref="SlevomatCodingStandard.ControlStructures.RequireNullCoalesceEqualOperator"/>
<rule ref="SlevomatCodingStandard.ControlStructures.RequireNullSafeObjectOperator"/>
<rule ref="SlevomatCodingStandard.Classes.RequireSelfReference"/>
<rule ref="SlevomatCodingStandard.TypeHints.NullTypeHintOnLastPosition"/>

<!-- phpcs Zend sniffs -->
<rule ref="Zend.NamingConventions.ValidVariableName">
Expand Down
Loading