squirrelphp/strings

PHP中的常用字符串操作:过滤字符串、生成随机字符串、将整数压缩成字符串以及修改URL

v1.1 2023-12-01 09:22 UTC

This package is auto-updated.

Last update: 2024-08-30 01:50:12 UTC


README

Build Status Test Coverage PHPStan Packagist Version PHP Version Software License

处理PHP应用程序中的常用字符串操作

安装

composer require squirrelphp/strings

或者,可以通过 squirrelphp/strings-bundle 进行安装,以便在Symfony项目中轻松集成,因为它默认提供了许多依赖注入服务,并允许您根据此库轻松注册额外的功能。

过滤字符串

使用 Squirrel\Strings\StringFilterInterface 接口过滤字符串并返回字符串

public function filter(string $string): string;

每个过滤器恰好做一件事情(理想情况下),因此可以根据输入需要如何更改/处理来组合它们。

可以通过实现 Squirrel\Strings\StringFilterInterface 简单地定义额外的过滤器。在应用程序中自定义过滤器的可能想法

  • 处理HTML标签,通常与应用程序高度相关(在哪些上下文中允许哪些标签)
  • 简化用户输入并将多个过滤器组合成一个过滤器
  • 将HTML转换为Markdown,或反之

此库有一些基本的过滤器,可以覆盖很多领域,可以单独使用或组合使用。每个过滤器类都以 Filter 后缀结尾,为了使标题和文本更易读,以下列表中没有提及。

换行符、制表符和空格

规范化换行符并删除多余的空格有助于在存储数据前节省空间,并始终得到一致的数据,特别是如果您希望在以后将空格和换行符转换为HTML或其他格式。

NormalizeNewlinesToUnixStyle

将所有类型的换行符(在Unicode中目前有8种不同的换行符)替换为Unix换行符(\n),这样就不会出现不同换行符的混合以及意外的结果。

ReplaceUnicodeWhitespaces

将所有Unicode空白字符(目前有16种不同)替换为普通空格,如果您不关心空格之间细微的差异(如宽度、非断开或数学),这在大多数输入中都是有意义的,以便能够剪除并限制不必要的空格。

RemoveExcessSpaces

删除任何不必要的空格,这些空格是

  • 字符串开头或结尾的任何空格
  • Unix换行符周围的任何空格
  • 将连续的空格缩减为仅一个空格

仅操作常规空格(unicode 0020,十进制 32)并忽略其他unicode空白字符。

LimitConsecutiveUnixNewlines

限制连续出现的新行数量。这仅处理Unix新行,不考虑新行之间的空格,因此首先运行上述三个过滤器是有意义的。

该过滤器的第一个参数是允许的连续新行数,默认为两个,但可以设置为任何大于零的数字。

RemoveZeroWidthSpaces

零宽度空格通常不是你希望保存在数据库中的内容,因此该过滤器移除了Unicode中定义的三个主要零宽度空格。

ReplaceNewlinesWithSpaces

如果你不需要文本中的任何新行,或者不允许新行,该过滤器将任何类型的新行(在Unicode中目前有8种不同的新行字符)替换为空格(以避免仅由新行分隔的内容组合的风险)。内部首先使用NormalizeNewlinesToUnixStyle过滤器,然后将Unix样式的新行替换为空格。

Trim

从字符串的开始和结束处删除字符,如果传递给构造函数的字符仅是ASCII,则使用PHP trim函数,如果修剪的是Unicode字符,则使用正则表达式。

默认情况下(如果没有使用构造函数参数),修剪的字符与PHP trim函数默认修剪的相同,即:" \t\n\r\0\x0B"(普通空格、水平制表符、换行符、回车符、空字符和垂直制表符)

ReplaceTabsWithSpaces

将所有水平制表符替换为空格。这是唯一处理水平制表符的过滤器,因为制表符可能与其他Unicode空格具有不同的/特定的含义。

WrapLongWordsNoHTML

没有换行符(如空格或换行符)的字符序列可能会破坏布局并且难以显示,这可能会出现在用户输入中,甚至可能在常规内容中意外出现。

该过滤器在未出现Unix新行或普通空格的一定数量的字符(默认为20)后添加一个零宽度空格。因此,如果空间足够,即使长词也不会被分割,但如果空间紧张,长词将被分割成多行。

此过滤器变体假设不允许HTML,因此它可能会分割任何长字符序列。

WrapLongWordsWithHTML

没有换行符(如空格或换行符)的字符序列可能会破坏布局并且难以显示,这可能会出现在用户输入中,甚至可能在常规内容中意外出现。

该过滤器在未出现Unix新行或普通空格的一定数量的字符(默认为20)后添加一个零宽度空格。因此,如果空间足够,即使长词也不会被分割,但如果空间紧张,长词将被分割成多行。

此过滤器变体在字符串中查找HTML标签。如果没有找到,则像WrapLongWordsNoHTML一样行为,如果存在HTML标签,则每个标签临时替换为替代字符,并只计算为一个字符,不会被过滤器分割。因此,当存在许多HTML标签时,单词可能会“过早”分割,因为HTML标签在换行时计为一个字符。

Cases: lowercase, uppercase, camelcase, snakecase

Lowercase

将所有Unicode字符转换为它们的等效小写。

Uppercase

将所有Unicode字符转换为它们的等效大写。

UppercaseFirstCharacter

将字符串的第一个字符转换为大写,正确处理Unicode字符作为第一个字符。

UppercaseWordsFirstCharacter

将字符串中每个单词的第一个字符转换为大写,正确处理Unicode字符。

CamelCaseToSnakeCase

从CamelCase转换为snake_case。仅支持字母数字字符(A-Z、a-z、0-9),忽略所有其他字符!

SnakeCaseToCamelCase

从snake_case转换为CamelCase。仅支持字母数字字符(A-Z、a-z、0-9),忽略所有其他字符!

HTML

RemoveHTMLTags

移除所有HTML标签。如果HTML标签不规范,可能会移除比预期更多的内容,因为它不尝试验证HTML,只是移除任何看起来像HTML标签的内容。

RemoveHTMLTagCharacters

移除HTML标签中使用的三个主要字符:<,>和""

ReplaceUnixStyleNewlinesWithParagraphs

对于HTML,你通常希望以可预测的方式处理换行符,这个过滤器是其中一种可能

  • 将双换行符\n\n转换为</p><p>
  • 将单行换行符\n转换为<br/>
  • 在字符串开头添加<p>并在结尾添加</p>

对于没有块级HTML标签的简单内容,这通常是理想的结构化文本并在HTML页面上显示的方式。

EncodeBasicHTMLEntities

&"'<>编码为其HTML实体(&amp;&quot;&apos;&lt;&gt;),这对于在HTML环境中正确和安全地显示文本非常有帮助。

DecodeBasicHTMLEntities

EncodeBasicHTMLEntities相反,如果你知道输入可能包含HTML实体,并且你想简化文本并避免像&amp;amp;这样的东西,这是有意义的。

DecodeAllHTMLEntities

根据HTML5标准解码所有HTML实体(内部使用html_entity_decode)。这通常不是必需的,但如果你知道收到的文本包含很多HTML实体,而且你不知道确切的种类或数量,这可能会很有意义。

移除/限制字符和内容

RemoveNonUTF8Characters

移除任何不符合UTF8规范的字符。这个过滤器建议用于来自你应用外部的任何内容(用户输入、Web服务、数据导入),这样你就可以继续在有效的UTF8字符串上操作。大多数字符串函数或数据库在字符串包含无效字符时将拒绝该字符串。

如果你的应用程序绝对不能出现无效的UTF8字符,那么检查应用程序中的编码并以这种方式抛出异常可能更有意义。

// Checks if $string contains only valid UTF8 characters
if (!\mb_check_encoding($string, 'UTF-8')) {
  // Invalid characters found, log this or throw an exception
}

然而,对于通用的用户输入,你可能只想尝试解决问题,即使输入部分损坏(可能比完全失败更好)。

RemoveNonAlphanumeric

移除任何不是字母或数字的字符,因此只允许A-Z、a-z和0-9。对于标记、URL的一部分、输入的代码或其他你知道不允许其他字符的情况,这可能很有用,你只想忽略非字母数字的任何内容。

RemoveNonAlphabetic

移除任何不是字母的字符,因此只允许A-Z和a-z。对于标记、国家或语言代码或其他你知道不允许其他字符的情况,这可能很有用,你只想忽略它们。

RemoveNonNumeric

移除任何不是数字的字符,因此只允许0-9。对于标记、URL的一部分、输入的代码或其他你知道不允许其他字符的情况,这可能很有用,你只想忽略它们。

RemoveNonAsciiAndControlCharacters

移除任何不是字母、数字和基本ASCII字符的字符,因此只允许A-Z、a-z、0-9、空格和!"#$'()*+,-./:;<=>?@[\]^_`{|}~(不允许换行符、控制字符或unicode - 所有这些都已移除)。

RemoveEmails

移除任何看起来像电子邮件地址的内容,这意味着任何包含非空格字符的字符串部分,在@符号前后。

最初添加是为了能够分析文本并检测语言,因为电子邮件地址可能会使语言检测算法混乱,因此从字符串中移除任何看起来像电子邮件地址的内容应该导致“只有文本”或至少是更可分析的文本。

RemoveURLs

移除任何看起来像URL的内容,这意味着任何看起来像有效方案的字符串部分,后跟"://",后跟零个或多个非空格字符。

最初添加是为了能够分析文本和检测语言,因为URL会干扰语言检测算法,所以从字符串中删除任何看起来像URL的东西应该导致“只有文本”或至少更容易分析的文本。

转换为ASCII

有时Unicode字符众多可能会成为障碍 - 例如在这些情况下

  • 在受限客户数据库中,您希望输入的姓氏如émil也能与emil匹配,这样客户就不能稍微更改他的名字来规避您的安全措施
  • 当用户输入地址,您要将其与已知地址数据库进行核对时,希望它是用户友好的,因此如果用户输入Leon Breitling-StrasseLeon Breitlingstrasse,您希望这两个都能匹配Léon Breitling-Strasse,尽管字符不完全匹配 1:1
  • 对于URL,您希望尽可能地将字符映射到ASCII字母,因此标题为L'école d'Humanité的博客文章变为l-ecole-d-humanite(对于URL如https://my-blog.com/2019-07-12/l-ecole-d-humanite),这对于用户和搜索引擎来说都是可读的

注意:这对于使用拉丁字符的国家和语言(如大多数欧洲、北美、南美、大多数非洲、澳大利亚)效果很好,但对于其他脚本(如西里尔文、阿拉伯文、汉字或希腊文等)效果则不那么好。

NormalizeLettersToAscii

尽可能将大多数字母减少到它们的基拉丁ASCII字符(A-Z,a-z),例如,é变为e,Â变为A等。它非常彻底,并使用Intl扩展的Normalizer以及一系列自定义转换。某些字符被转换为两个ASCII字符(如Æ => AE,或ß变为ss),因此您的字符串可能会更长。

NormalizeToAlphanumeric

运行上面的NormalizeLettersToAscii,然后删除任何非字母数字字符,因此

  • Léon Breitling-Strasse 13变为LeonBreitlingStrasse13
  • 'Pré Raguel Strasse de l'école'变为'PreRaguelStrassedelecole'

NormalizeToAlphanumericLowercase

运行上面的NormalizeToAlphanumeric,然后将所有字符转换为小写,因此

  • Léon Breitling-Strasse 13变为leonbreitlingstrasse13
  • 'Pré Raguel Strasse de l'école'变为'preraguelstrassedelecole'

如果您以这种方式处理用户输入和数据库中的已知值,您就可以匹配它们并得到更多的匹配/结果,因为空格、连字符、变音符号等不被考虑。

ReplaceNonAlphanumeric

有时您不想删除非字母数字字符,而是用字符替换它们,例如对于URL,您希望将L'école d'Humanité转换为l-ecole-d-humanite

这就是ReplaceNonAlphanumeric默认执行的操作 - 将所有非字母数字字符替换为连字符,如果多个非字母数字字符连续出现,则只替换为一个连字符。

ReplaceNonAlphanumeric的构造函数中,您可以设置一个除连字符之外的替换字符 - 例如,点或斜杠,具体取决于您的用例。

Streamline input

这些过滤器将其他过滤器组合成一个合理的包,以在用户输入上运行,更多的是一个示例,而不是您可能希望直接在应用程序中使用的示例。

您可以通过使用Squirrel\Strings\StringFilterRunner类来执行自己的过滤器组合。

StreamlineInputWithNewlines

运行以下过滤器

这确保字符串是有效的UTF8,并规范化所有空白字符,同时删除不必要的空白字符,而内容本身保持不变(与HTML一起工作或单独工作,不转换HTML实体)。

StreamlineInputNoNewlines

运行以下过滤器

基本与StreamlineInputWithNewlines相同,但换行符会被转换为空格。这对于像姓名、电子邮件地址和其他任何换行符没有意义的普通用户输入很有用。

测试一个字符串

使用Squirrel\Strings\StringTesterInterface接口测试一个字符串,并返回true或false(测试是否成功)

public function test(string $string): bool;

可以通过实现Squirrel\Strings\StringTesterInterface轻松定义额外的测试器。在应用程序中自定义测试器的可能想法

  • 检查外部数据结构(这可以高度依赖于应用程序)
  • 检查字符串是否包含允许的值或允许的字符

此库有两个默认测试器。每个测试器类都以Tester后缀结尾,以下列表中没有提及,以保持标题和文本的清晰性。

ValidUTF8

检查字符串是否只包含有效的UTF8字符。如果您的应用程序对外部数据要求严格,则拒绝任何包含非UTF8字符的数据是有意义的(处理非UTF8字符的一种不太严格的方法是RemoveNonUTF8Characters过滤器)。

ValidDateTime

根据类构造函数中提供的date函数接受的日期时间格式(默认为ISO日期格式,年份、月份和日期之间用破折号分隔,即Y-m-d)检查字符串,并确保给定的日期存在(2021-02-29将返回false,而2020-02-29将返回true)。在验证输入时,这可以轻松确保日期符合您期望的格式,并可进一步处理。

生成一个随机字符串

根据字符串中允许的字符列表生成随机字符串。

包含两个类(一个支持unicode,一个只支持ASCII)可以轻松定义一个具有您自己的字符集的随机生成器,这些字符可以出现在随机字符串中。以下是一些合理的值

  • ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,每个字符有62种可能的值,可以是A-Z、a-z或0-9,这些值在应用程序中使用非常安全(没有特殊字符,只有字母数字)
  • abcdefghijklmnopqrstuvwxyz0123456789,每个字符有36种可能的值,与上面相同,但这是不区分大小写的版本,用于当应该区分“A”和“a”时(例如)
  • 234579ACDEFGHKMNPQRSTUVWXYZ234579acdefghkmnpqrstuvwxyz,每个字符有27个易于阅读的大写或小写字母:如果某人需要输入一个代码,最好避免容易混淆的字符,如0(数字零)和O(字母),或8(数字八)和B(字母)

定义您自己的可能字符范围很容易,甚至可以使用unicode字符。

将字符串压缩为数字

将整数转换为具有给定“字符集”的字符串,这样我们可以将整数编码以压缩它(因此一个包含8个数字的整数现在只是一个4个字符的字符串),并在需要时将其转换回。

主要用例是URL中的令牌,这样就需要更少的空间,因为即使是大数字,如果使用每个字符36或62个可能的值,也会变成短字符串:使用62个可能的字符(ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789),一个三字符的字符串可以覆盖多达238,328个数字,五个字符可以覆盖多达916,132,832个数字。

压缩的副作用是它使整数的使用不再那么明显——令牌看起来是随机的,不会泄露它们的意图。

定义您自己的可能字符范围很容易,甚至可以使用unicode字符。

URL

URL类接受一个URL作为构造函数参数,然后允许您获取或更改URL的某些部分以执行以下操作

  • 获取方案、主机、路径、查询字符串和特定的查询字符串变量
  • 将绝对URL转换为相对URL
  • 更改方案、主机、路径和查询字符串
  • 替换查询字符串变量,或添加/删除它们

这可以用来轻松构建或更改您的URL,或者用于清理给定的URL的某些部分,例如在重定向时:使用相对URL而不是绝对URL,以避免恶意重定向到您无法控制的地方。

正则表达式包装器

由于使用了引用($matches)和许多可能的返回值,因此使用内置的 preg_matchpreg_match_allpreg_replacepreg_replace_callback PHP 函数通常会使代码的可读性降低,难以理解,对于静态分析器来说也是如此。 Squirrel\Strings\Regex 包装了这些 preg 函数的基本功能,创建了更容易理解的返回值,并在出现错误时抛出 Squirrel\Strings\Exception\RegexException。以下是 Regex 类的可用静态方法

Regex::isMatch(string $pattern, string $subject, int $offset): bool

包装 preg_match 以检查 $pattern 是否存在于 $subject 中。

Regex::getMatches(string $pattern, string $subject, int $flags, int $offset): ?array

包装 preg_match_all 以检索 $subject$pattern 的所有匹配项,始终设置 PREG_UNMATCHED_AS_NULL 标志,并可以添加额外的标志。如果没有找到匹配项,则返回 null,否则返回由 preg_match_all$matches 设置的结果数组。

Regex::replace(string|array $pattern, string|array $replacement, string $subject, int $limit): string

包装 preg_replace 以替换 $pattern 的匹配项为 $replacement,并且只接受字符串作为 $subject

Regex::replaceArray(string|array $pattern, string|array $replacement, array $subject, int $limit): array

包装 preg_replace 以替换 $pattern 的匹配项为 $replacement,并且只接受数组作为 $subject

Regex::replaceWithCallback(string|array $pattern, callback $callback, string $subject, int $limit, int $flags): string

包装 preg_replace_callback 以对每个 $pattern$subject 中的匹配项调用一个回调,该回调具有签名 function(array $matches): string,并且只接受字符串作为 $subject

Regex::replaceArrayWithCallback(string|array $pattern, callback $callback, array $subject, int $limit, int $flags): array

包装 preg_replace_callback 以对每个 $pattern$subject 中的匹配项调用一个回调,该回调具有签名 function(array $matches): string,并且只接受数组作为 $subject