【问题标题】:Identify if a string is camelCase识别字符串是否为驼峰式
【发布时间】:2015-05-11 00:38:10
【问题描述】:

我正在尝试让 PHP_CodeSniffer 检查类名中的 camelCase,但在我看来,camelCase 检查是不可能的(没有字典,包括 techy 词)。

我已经在互联网上搜索过,但到目前为止,我看到的唯一选择是字符串是否有一些常见的分隔符可以从中分解 - 即下划线、单词之间的空格等。

即使这也没有用,因为只有在名称​​准确/始终在每个单词之间包含分隔符时,检查才能准确。
“检查”的重点是确定名称是否格式不正确,这可能包括不正确分隔。

此外,PHP_CodeSniffer 上的资源要么很少见,要么非常基础和技术含量高,只有作者/开发人员才能理解。

当前标准嗅探检查

我在一些当前的 Sniffs(即 Squiz 和 PEAR 标准)中发现了这段代码:

if (PHP_CodeSniffer::isCamelCaps($functionName, false, true, false) === false) 

但是,我查看了 PHP_CodeSniffer 核心代码,该函数仅执行以下操作:

// Check the first character first.
// Check that the name only contains legal characters.
// Check that there are not two capital letters next to each other.
// The character is a number, so it cant be a capital.

这些基本检查总比没有好,尽管对于它们预期的目的来说可能毫无用处,因为它们根本不检查骆驼情况。

问题

嗅探器(或即 PHP 脚本)如何知道在给定字符串中检查哪些“单词”以识别字符串是否为 100% 驼峰式?


编辑

示例

正确的驼峰式:class calculateAdminLoginCount

// Not camelCase
class calculateadminlogincount

// Partially camelCase
class calculateadminLogincount

isCamelCaps() 函数(或与此相关的任何 PHP 脚本)如何捕获上述两个示例?

如果函数或 PHP 脚本没有“单词”的概念而不提供该信息(即来自字典),它如何从字符串中识别“单独的单词”?

即使脚本在哪里爆炸,它会根据什么爆炸?

参加class calculateadminLogincount
任何PHP脚本如何识别calculateadminLogincount是该字符串中的不同单词,然后能够检查:第一个字母第一个单词是小写,然后所有后续单词第一个字母是大写?

isCamelCaps()函数

public static function isCamelCaps(
    $string,
    $classFormat=false,
    $public=true,
    $strict=true
) {

        // Check the first character first.
        if ($classFormat === false) {
            $legalFirstChar = '';
            if ($public === false) {
                $legalFirstChar = '[_]';
            }

            if ($strict === false) {
                // Can either start with a lowercase letter, 
                // or multiple uppercase
                // in a row, representing an acronym.
                $legalFirstChar .= '([A-Z]{2,}|[a-z])';
            } else {
                $legalFirstChar .= '[a-z]';
            }
        } else {
            $legalFirstChar = '[A-Z]';
        }

        if (preg_match("/^$legalFirstChar/", $string) === 0) {
            return false;
        }

        // Check that the name only contains legal characters.
        $legalChars = 'a-zA-Z0-9';
        if (preg_match("|[^$legalChars]|", substr($string, 1)) > 0) {
            return false;
        }

        if ($strict === true) {
            // Check that there are not two capital letters 
            // next to each other.
            $length          = strlen($string);
            $lastCharWasCaps = $classFormat;

            for ($i = 1; $i < $length; $i++) {
                $ascii = ord($string{$i});
                if ($ascii >= 48 && $ascii <= 57) {
                    // The character is a number, so it cant be a capital.
                    $isCaps = false;
                } else {
                    if (strtoupper($string{$i}) === $string{$i}) {
                        $isCaps = true;
                    } else {
                        $isCaps = false;
                    }
                }

                if ($isCaps === true && $lastCharWasCaps === true) {
                    return false;
                }

                $lastCharWasCaps = $isCaps;
            }
        }//end if

        return true;

    }//end isCamelCaps()

编辑 2

对于那些想知道这是否值得,或者我只是在“玩弄”和“玩得开心”的人的一些信息:

必须正确命名类名,因为文件/文件夹结构以及名称和类名必须匹配才能使自动加载器稳定工作。

虽然我检查了核心代码本身以检查和处理无法加载脚本、类等的此类问题(当然),但附加脚本 (PHP_CodeSniffer) 运行所有文件并告诉我没有任何问题可能存在潜在问题的地方。
即使只是为了进行第二次检查,尤其是因为它还可以确保代码库整洁、结构正确并且始终具有连续性。

【问题讨论】:

  • 您能否举一些导致问题的字符串示例,其中某些内容被识别为 camelCase 但不是,反之亦然?
  • 我不明白isCamelCaps 测试是如何不足的。哪些示例未能通过这些测试?
  • 嗯,这非常棘手而且可能是不可能的。我能想到的最好的方法是“容忍”限制,以在可能失败时发出警告(但不是确定的)。例如,如果有 20 个字符且只有一个大写字母,那就是“可能失败”。跨度>
  • @sjagr char count 可能是一个有用的想法。不会是准确的,因为可以有长词,短词等,但再加上其他检查,它可能是我可以玩的东西。无论我最终完成什么检查,它们都只是一个可以忽略的警告/通知,或者有时可能最终有用。
  • 正是我的意思!也许也可以将它与字典相结合。一般来说,如果你编程正确,你不会在 camelCase 变量或类名中有冗长的单词。

标签: php camelcasing phpcodesniffer


【解决方案1】:

您可以通过拆分大小写转换的单词来分析函数名称的大小写是否正确。对于原始函数名称的每一部分,在字典或字典 + 行话文件('calc'、'url'、'admin' 等(可能先检查行话))中查找该子词。如果任何子词失败,则表示没有正确的大写。

您可以使用 Solr 或 ElasticSearch 通过 Lucene 中的 WordDelimiterFilter 为您拆分单词。这将在大小写更改时创建子词:

"PowerShot" -> "Power" "Shot" "LoginURL" => "Login" "URL"

您可以将单词直接插入这些 NoSQL 数据库并稍后进行分析,或者您可以(至少在 ES 中)简单地使用单词分隔符标记过滤器来拆分您的查询而不实际保存结果。

http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/analysis-word-delimiter-tokenfilter.html

https://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters#solr.WordDelimiterFilterFactory

例子:

calcAdminLogin => 计算管理员登录

calcadminlogin => calcadminlogin

如果您有一个包含诸如“calc”和“admin”之类的词的补充字典,那么第一个函数名称将分解为字典中出现的 3 个词,因此骆驼大小写是正确的。

在第二个示例中,'calcadminlogin' 将无法在字典中找到,因此骆驼大小写不正确。

【讨论】:

  • "拆分大小写转换的单词" 但是你依赖于使用脚本正在检查错误的东西。camelCase 检查器是为了找到有些开发人员没有正确使用camelCase。如果他们没有正确使用 camelCase,那么将没有 case transition 可以中断/拆分,因此使用 case transition 的检查器将变得无用。仅当名称正确使用驼峰式时,它才会起作用。正如我在我的问题中概述的那样 - 如果不使用字典,似乎不可能拆分包含“单词”的字符串。除非我误解了?
  • " 如果他们没有正确使用 camelCase,那么不会有大小写转换到 break/split from" ... 因此字典查找将失败。
  • 字典不会因为不正确的大小写而失败(可能使用不区分大小写)。问题是更多的非字典词,技术词等。 calcUsrLoginCount。字典方法需要做很多工作,在 PHP_CodeSniffer 等中添加一些调用的数据库,并且由于它不会 100% 准确,我认为它不值得追求。因此,我提出这个问题是为了向潜伏的技术人员寻求其他答案。
  • 它与不区分大小写或字典敏感搜索无关,请阅读更新示例。
  • "在第二个示例中,'calcadminlogin' 将无法在字典中找到"。没有。粗略的例子:循环整个字典数据; $dictWord = "Current_Loop_Dictionary_Word"(我们现在说“管理员”)$string = "calcadminlogin"。并且 IF $dictWord$string 中找到(简单的 strpos() 搜索),在位置 0 处是 $dictWord,如果 TRUE 是首字母小写,ELSE POSITION !== 0 是首字母大写。一本字典会使这件事变得简单,但要花很多时间/工作。考虑到技术词汇、技术缩写等,它不会 100%准确。
【解决方案2】:

我制作了一些脚本来尝试“松散地”识别类名是否为 camelCase。

我为我的场景编写的一些脚本对其他人没有帮助,例如它们特定于我自己的命名约定(我没有将它们包含在此处)。
因此,我实际收集的脚本使这一切都值得,但希望下面更通用的脚本对其他人有所帮助。

例如,我用小写单词作为类名的前缀,因此我检查该前缀后面的单词是否为大写。
对于那些(大多数人)不使用特定单词作为类名前缀的人,那么很容易检查字符串的第一个字符是否为小写。

非常欢迎批评。


只允许混合大小写字母

这可确保类名仅包含大写或小写字母 (Az),这是 camelCase 检查所必需的(如果删除此脚本,则需要更改其他脚本以适应潜在的非字母字符)。

/** Check string is only alpha (A-z) */
if (ctype_alpha($name) === false) {
  $error = '%s name must only contain alpha chars (A-z)';
  $phpcsFile->addError($error, $stackPtr, 'AlphaChars', $errorData);
  return;
}

两个大写字符不能放在一起

一些标准允许使用首字母缩略词等,但我的标准不允许这样做,因为它不是严格的驼峰式,并且会破坏阅读流程。

例如userSitePHPLogin 无效,userSitePhpLogin 有效。

(可能有一种更优雅的方法可以做到这一点,但它工作正常,并且考虑到它是针对 PHP_CodeSniffer 的,我不需要微优化)

/** Check for uppercase chars together */
$nameUppercaseExplode = preg_split('/(?=[A-Z])/', $name);
$totalIllegalUpperChars = 0;

foreach ($nameUppercaseExplode as $namePiece) {
  if (strlen($namePiece) == 1) {
    $totalIllegalUpperChars++;
  }
}

if ($totalIllegalUpperChars >0) {
  $warning = 'Class name seems invalid; 
  Total '.$totalIllegalUpperChars.' uppercase chars not part of camelCase';
  $phpcsFile->addWarning($warning, $stackPtr, 'UppercaseTogether', $errorData);
}

例如类名DUserPHPUserclassLogin 返回:

类名似乎无效;总共 4 个大写字符不属于 camelCase

它并不完美,因为它在这张支票上只有 1 分。
但只有在至少出现 1 次大写字母时才会返回警告。

例如类名classDUserPhpUserLogin 返回:

类名似乎无效;总共 1 个大写字符不属于 camelCase

所以这至少会提示开发人员检查名称并根据需要进行修复。


检查总大写字符是否小于总字数

感谢sjagr 的创意。

“总字数”当然是一个“猜测”的数字,基于每个单词 5 个字符的平均值 - 因为官方的平均值似乎是每个单词平均 4.7 个字符。

/** Loose check if total (guessed) words not match total uppercase chars */
$totalWordsGuess = ceil(strlen($name) / 5);
$totalUpperChars = strlen(preg_replace('![^A-Z]+!', '', $name));

// Pointless if only 1 word (camelCase not exist)
if ($totalWordsGuess >1) {

  // Remove the first word which should be lowercase
  // (first word should be checked in separate check above this one)
  $totalWordsGuess--;

  if ($totalUpperChars < $totalWordsGuess) {
    $warning = 'Expected '.$totalWordsGuess.' camelCase words in class name; 
    Found '.$totalUpperChars;
    $phpcsFile->addWarning($warning, $stackPtr, 'BadCamelCase', $errorData);
  }

}

我已经对其进行了测试并且效果很好(这只是对潜在问题的警告)。

例如,使用类名UserLoginToomanywordsWithoutcamelCase,PHP_CodeSniffer 返回:

类名中预期有 7 个 camelCase 单词;找到 5 个

如果返回的误报过多(不同的开发人员使用不同的词等),则将当前的“5”向上或向下调整一个档次。

编辑:更新了上面的脚本:

  • 添加了条件,因此脚本仅在超过 1 个单词时运行,如 1 个单词 不能是驼峰式。
  • 添加了从总猜测词中减去 1 的代码 (var --),以 考虑到第一个单词是小写的,所以没有大写计数 会为它而存在。

您应该在此之上单独检查以检查第一个单词,如果第一个单词不是小写,则 returns

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-11
    • 1970-01-01
    • 2017-09-05
    • 2023-02-20
    • 1970-01-01
    相关资源
    最近更新 更多