Skip to content

Commit

Permalink
Merge 2156b59 into 9676824
Browse files Browse the repository at this point in the history
  • Loading branch information
jrfnl committed Mar 26, 2020
2 parents 9676824 + 2156b59 commit 50d3e44
Show file tree
Hide file tree
Showing 3 changed files with 344 additions and 0 deletions.
113 changes: 113 additions & 0 deletions PHPCSUtils/Utils/NamingConventions.php
@@ -0,0 +1,113 @@
<?php
/**
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
*
* @package PHPCSUtils
* @copyright 2019-2020 PHPCSUtils Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSUtils
*/

namespace PHPCSUtils\Utils;

/**
* Utility functions for working with identifier names.
*
* Identifiers in PHP are namespace names, class/trait/interface names, function names,
* variable names and constant names.
*
* @since 1.0.0
*/
class NamingConventions
{

/**
* Regular expression to check if a given identifier name is valid for use in PHP.
*
* @link http://php.net/manual/en/language.variables.basics.php
* @link http://php.net/manual/en/language.constants.php
* @link http://php.net/manual/en/functions.user-defined.php
* @link http://php.net/manual/en/language.oop5.basic.php
*
* @since 1.0.0
*
* @var string
*/
const PHP_LABEL_REGEX = '`^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$`';

/**
* Uppercase A-Z.
*
* @since 1.0.0
*
* @var string
*/
const AZ_UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

/**
* Lowercase a-z.
*
* @since 1.0.0
*
* @var string
*/
const AZ_LOWER = 'abcdefghijklmnopqrstuvwxyz';

/**
* Verify whether an arbitrary text string is valid as an identifier name in PHP.
*
* For variable names: the leading `$` needs to be removed prior to passing the name to this method.
*
* @since 1.0.0
*
* @param string $name The name.
*
* @return bool
*/
public static function isValidIdentifierName($name)
{
if (is_string($name) === false || $name === '' || \strpos($name, ' ') !== false) {
return false;
}

return (\preg_match(self::PHP_LABEL_REGEX, $name) === 1);
}

/**
* Check if two arbitrary identifier names will be seen as the same in PHP.
*
* This method should not be used for variable or constant names, but *should* be used
* when comparing namespace, class/trait/interface and function names.
*
* Variable and constant names in PHP are case-sensitive, except for constants explicitely
* declared case-insensitive using the third parameter for `define()`.
*
* All other names are case-insensitive for the most part, but as it's PHP, not completely.
* Basically ASCII chars used are case-insensitive, but anything from 0x80 up is case-sensitive.
*
* This method takes this case-(in)sensitivity into account when comparing identifier names.
*
* Note: this method does not check whether the passed names would be valid for identifiers!
* See the {@see \PHPCSUtils\Utils\NamingConventions::isValidIdentifierName()} method.
*
* @since 1.0.0
*
* @param string $nameA The first identifier name.
* @param string $nameB The second identifier name.
*
* @return bool TRUE if these names would be considered the same in PHP, FALSE otherwise.
*/
public static function isEqual($nameA, $nameB)
{
// Simple quick check first.
if ($nameA === $nameB) {
return true;
}

// OK, so these may be different names or they may be the same name with case differences.
$nameA = \strtr($nameA, self::AZ_UPPER, self::AZ_LOWER);
$nameB = \strtr($nameB, self::AZ_UPPER, self::AZ_LOWER);

return ($nameA === $nameB);
}
}
101 changes: 101 additions & 0 deletions Tests/Utils/NamingConventions/IsEqualTest.php
@@ -0,0 +1,101 @@
<?php
/**
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
*
* @package PHPCSUtils
* @copyright 2019-2020 PHPCSUtils Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSUtils
*/

namespace PHPCSUtils\Tests\Utils\NamingConventions;

use PHPCSUtils\Utils\NamingConventions;
use PHPUnit\Framework\TestCase;

/**
* Tests for the \PHPCSUtils\Utils\NamingConventions::isEqual() method.
*
* @covers \PHPCSUtils\Utils\NamingConventions::isEqual
*
* @group namingconventions
*
* @since 1.0.0
*/
class IsEqualTest extends TestCase
{

/**
* Test whether two arbitrary strings are considered equal for PHP identifier names.
*
* @dataProvider dataIsEqual
*
* @param string $inputA The first name.
* @param string $inputB The second name.
* @param array $expected The expected function output.
*
* @return void
*/
public function testIsEqual($inputA, $inputB, $expected)
{
$this->assertSame($expected, NamingConventions::isEqual($inputA, $inputB));
}

/**
* Data provider.
*
* @see testIsEqual() For the array format.
*
* @return array
*/
public function dataIsEqual()
{
return [
'a-z-0-9-only-same-case' => [
'abcdefghijklmnopqrstuvwxyz_0123456789',
'abcdefghijklmnopqrstuvwxyz_0123456789',
true,
],
'a-z-0-9-only-different-case' => [
'ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789',
'abcdefghijklmnopqrstuvwxyz_0123456789',
true,
],
'extended-ascii-same-case' => [
'ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢áíóúñÑ',
'ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢áíóúñÑ',
true,
],
'extended-ascii-different-case' => [
'ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢áíóúñÑ',
'çÜÉÂÄÀÅÇÊËÈÏÎÌäåéÆæÔÖÒÛÙŸöü¢ÁÍÓÚÑñ',
false,
],
'mixed-ascii-extended-ascii-same-case' => [
'Déjàvü',
'Déjàvü',
true,
],
'mixed-ascii-extended-ascii-different-case-only-for-ascii' => [
'Déjàvü',
'déJàVü',
true,
],
'mixed-ascii-extended-ascii-different-case' => [
'Déjàvü',
'DÉJÀVÜ',
false,
],
'emoji-name' => [
'💩💩💩',
'💩💩💩',
true,
],
'invalid-input-but-not-relevant' => [
true,
true,
true,
],
];
}
}
130 changes: 130 additions & 0 deletions Tests/Utils/NamingConventions/IsValidIdentifierNameTest.php
@@ -0,0 +1,130 @@
<?php
/**
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
*
* @package PHPCSUtils
* @copyright 2019-2020 PHPCSUtils Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSUtils
*/

namespace PHPCSUtils\Tests\Utils\NamingConventions;

use PHPCSUtils\Utils\NamingConventions;
use PHPUnit\Framework\TestCase;

/**
* Tests for the \PHPCSUtils\Utils\NamingConventions::isValidIdentifierName() method.
*
* @covers \PHPCSUtils\Utils\NamingConventions::isValidIdentifierName
*
* @group namingconventions
*
* @since 1.0.0
*/
class IsValidIdentifierNameTest extends TestCase
{

/**
* Test correctly detecting whether an arbitrary string can be a valid PHP identifier name.
*
* @dataProvider dataIsValidIdentifierName
*
* @param string $input The input string.
* @param array $expected The expected function output.
*
* @return void
*/
public function testIsValidIdentifierName($input, $expected)
{
$this->assertSame($expected, NamingConventions::isValidIdentifierName($input));
}

/**
* Data provider.
*
* @see testIsValidIdentifierName() For the array format.
*
* @return array
*/
public function dataIsValidIdentifierName()
{
return [
// Valid names.
'a-z-only' => [
'valid_name',
true,
],
'a-z-uppercase' => [
'VALID_NAME',
true,
],
'a-z-camel-caps' => [
'Valid_Name',
true,
],
'alphanum-mixed-case' => [
'VaLiD128NaMe',
true,
],
'underscore-prefix' => [
'_valid_name',
true,
],
'double-underscore-prefix' => [
'__valid_name',
true,
],
'extended-ascii-lowercase' => [
'пасха',
true,
],
'extended-ascii-mixed-case' => [
'Пасха',
true,
],
'extended-ascii-non-letter' => [
'¢£¥ƒ¿½¼«»±÷˜°²',
true,
],
'emoji-name-1' => [
'💩💩💩',
true,
],
'emoji-name-2' => [
'😎',
true,
],

// Invalid names.
'not-a-string' => [
12345,
false,
],
'empty-string' => [
'',
false,
],
'name-with-whitespace' => [
'aa bb',
false,
],
'starts-with-number' => [
'2beornot2be',
false,
],
'name-with-quotes-in-it' => [
"aa'1'",
false,
],
'name-with-dash' => [
'some-thing',
false,
],
'name-with-punctuation-chars' => [
'!@#$%&*(){}[]',
false,
],
];
}
}

0 comments on commit 50d3e44

Please sign in to comment.