肉粉碎机 / 格式
字符串格式化库。
Requires
- php: ^7.0
- ext-pcre: *
- fleshgrinder/core: ^1.0
- fleshgrinder/value: ^1.0
Requires (Dev)
- phpunit/phpunit: ^6.0
This package is not auto-updated.
Last update: 2020-01-24 16:37:35 UTC
README
格式
format库提供格式特殊字符串模式的功能。其实现类似于 sprintf
和 msgfmt_format_message
,但具有各种独特且非常实用的功能。
安装
打开终端,进入您的项目目录,并执行以下命令以将此库添加到您的依赖项
composer require fleshgrinder/format
此命令要求您全局安装了Composer,如Composer文档中的安装章节所述。
使用方法
此库具有一个用于格式化字符串模式的静态方法。格式方法的示例包括:
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('Hello, {}!', ['World']) === 'Hello, World!'); assert(Formatter::format('The number is {}', [1000]) === 'The number is 1,000'); assert(Formatter::format('{}', [[1, 2, 3]]) === '1, 2, 3'); assert(Formatter::format('{value}', ['value' => 42]) === '42'); assert(Formatter::format('{} {}', [1, 2]) === '1 2'); assert(Formatter::format('{.3}', [0.123456789]) === '0.123'); assert(Formatter::format('{.2:and}', [[1, 2, 3]]) === '1.00, 2.00, and 3.00'); assert(Formatter::format('{#b}', [2]) === '0b10'); assert(Formatter::format('{:?}', [tmpfile()]) === 'stream resource');
占位符
每个占位符都可以指定它引用的参数值,如果省略,则假定是“下一个参数”。例如,模式{} {} {}
将取三个参数,并将按照它们给出的顺序进行格式化。然而,模式{2} {1} {0}
将以相反的顺序格式化参数。
一旦开始混合这两种类型的占位符,事情可能会变得有点复杂。可以将“下一个参数”指定符视为参数的迭代器。每次遇到“下一个参数”指定符时,迭代器都会前进。这导致以下行为
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('{1} {} {0} {}', [1, 2]) === '2 1 1 2');
在看到第一个{}
之前,参数的内部迭代器没有前进,因此打印第一个参数。然后到达第二个{}
时,迭代器已经前进了,指向第二个参数。本质上,明确指定其参数的占位符不会影响未指定参数的占位符。
占位符不仅限于数字,还可以通过名称访问它们。名称限于A-Za-z0-9_-
字符,这确保了最高的兼容性,并且应该匹配PHP世界中99%的所有关联数组键。
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('{placeholder}', ['placeholder' => 'test']) === 'test'); assert(Formatter::format('{placeholder} {}', ['placeholder' => 2, 1]) === '2 1'); assert(Formatter::format('{a} {c} {b}', ['a' => 'a', 'b' => 'b', 'c' => 3]) === 'a 3 b');
必须使用所有占位符,但不需要所有参数。如果参数中缺少占位符,则会抛出MissingPlaceholderException
。
<?php use Fleshgrinder\Core\Formatter; use Fleshgrinder\Core\Formatter\MissingPlaceholderException; try { Formatter::format('{}', []); } catch (MissingPlaceholderException $e) { assert($e->getMessage() === 'Placeholder `0` missing from arguments, got empty array'); }
您可以在模式中多次引用相同的参数,包括不同的修饰符(将在下一节中解释)。
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('{a} {a} {a}', ['a' => 'foo']) === 'foo foo foo');
类型修饰符
类型修饰符可用于打印参数值的类型,而不是尝试格式化该值
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('{:?}', []) === 'void'); assert(Formatter::format('{:?}', [[]]) === 'array'); assert(Formatter::format('{:?}', [true]) === 'boolean'); assert(Formatter::format('{:?}', [false]) === 'boolean'); assert(Formatter::format('{:?}', [1.2]) === 'float'); assert(Formatter::format('{:?}', [1]) === 'integer'); assert(Formatter::format('{:?}', [null]) === 'null'); assert(Formatter::format('{:?}', [new stdClass]) === 'stdClass'); assert(Formatter::format('{:?}', [new DateTime]) === 'DateTime'); assert(Formatter::format('{:?}', [tmpfile()]) === 'stream resource'); assert(Formatter::format('{:?}', ['']) === 'string');
当然,可以与位置和命名占位符结合使用
<?php use Fleshgrinder\Core\{Formatter, Value}; assert( Formatter::format( 'Expected argument of type {expected}, got {actual:?}', ['expected' => Value::TYPE_ARRAY, 'actual' => ''] ) === 'Expected argument of type array, got string' );
使用fleshgrinder/value
库来获取值的类型,这意味着在实际上,类型名称在命名和大小写方面是一致的。然而,对于资源,输出略有扩展,包括资源的类型
<?php use Fleshgrinder\Core\{Formatter, Value}; $r = tmpfile(); assert(Value::getType($r) === 'resource'); assert(Formatter::format('{:?}', [$r]) === 'stream resource');
重要
类型修饰符不能与任何其他修饰符结合,并且始终覆盖其他所有内容!
字符串格式化和修饰符
该方法对二进制安全,字符串按原样打印。
从版本1.1.0开始,空字符串和可以转换为字符串的对象格式化为empty {:?}
,而不是完全无输出
<?php namespace Fleshgrinder\Examples; use Fleshgrinder\Core\Formatter; class Stringable { function __toString() { return ''; } } assert(Formatter::format('{}', ['']) === 'empty string'); assert(Formatter::format('{}', [new Stringable]) === 'empty Fleshgrinder\Examples\Stringable');
从1.1.0版本开始,还有插入符号表示法修饰符c
和用于ASCII控制字符的可打印Unicode替换修饰符p
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('{#c}', ["\0\n"]) === '^@^J'); assert(Formatter::format('{#p}', ["\0\n"]) === '␀␊');
这在构造错误消息时很有用,其中应包含违反某些约束的字符串,但由于控制字符的性质可能导致不希望的影响。这两个字符串修饰符有助于减轻这个问题。请注意,使用插入符号表示法可能无法重新创建原始字符串,这种情况下应使用可打印的Unicode替换。然而,这些字符是UTF-8字符而不是ASCII字符,这可能不可接受。
数字格式化和修饰符
数字使用number_format
及其默认的十进制和千位分隔符进行格式化,这确保了最佳的可读性和一致的格式。默认的小数位数为零,但可以通过在占位符后面的花括号内插入一个用点(.
)分隔的自然数来配置
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('{.3}', [1.23456]) === '1.235'); assert(Formatter::format('$ {.2}', [9999.99]) === '$ 9,999.99'); assert(Formatter::format('{0.3}', [1.2345]) === '1.235'); assert(Formatter::format('{a.2}', ['a' => 1]) === '1.00');
可以使用格式修饰符更改数字的输出格式。通过在占位符后面的花括号内插入一个井号(#
)后跟所需的修饰符来添加格式修饰符。可用的格式修饰符有
#b
用于二进制数字,#e
用于指数表示法,#o
用于八进制,#x
用于十六进制。
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('{#b}', [2]) === '0b10'); assert(Formatter::format('{#e}', [0.123456789]) === '1.234568e-1'); assert(Formatter::format('{#o}', [493]) === '0o755'); assert(Formatter::format('{#x}', [42]) === '0x2A');
请注意,没有#E
或#X
修饰符,这是故意的,以强制输出一致性。
可迭代列表和修饰符
可迭代数据结构格式化为逗号分隔的列表。可以通过在占位符后面的花括号内插入一个冒号(:
)后跟所需的单词来配置可选的连接词
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('{}', [[]]) === 'empty array'); assert(Formatter::format('{}', [new ArrayIterator]) === 'empty ArrayIterator'); assert(Formatter::format('{}', [['a']]) === 'a'); assert(Formatter::format('{}', [['a', 'b']]) === 'a, b'); assert(Formatter::format('{}', [['a', 'b', 'c']]) === 'a, b, c'); assert(Formatter::format('{:and}', [['a', 'b']]) === 'a and b'); assert(Formatter::format('{:or}', [['a', 'b', 'c']]) === 'a, b, or c');
这对于构造错误消息非常完美
<?php use Fleshgrinder\Core\Formatter; try { $expected = ['a', 'b', 'c']; $actual = 'x'; if (in_array($actual, $expected, true) === false) { throw new InvalidArgumentException(Formatter::format( 'Value must be one of {expected:or}, got {actual}', ['expected' => $expected, 'actual' => $actual] )); } } catch (InvalidArgumentException $e) { assert($e->getMessage() === 'Value must be one of a, b, or c, got x'); }
当然,除了类型之外,其他修饰符都可以与可迭代列表结合使用
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('{.1:and}', [[0, 1, 2]]) === '0.0, 1.0, and 2.0'); assert(Formatter::format('{#b:and}', [[0, 1, 2]]) === '0b0, 0b1, and 0b10'); assert(Formatter::format('{#o:and}', [[7, 8, 9]]) === '0o7, 0o10, and 0o11'); assert(Formatter::format('{#x:and}', [[9, 10, 11]]) === '0x9, 0xA, and 0xB');
请注意,可迭代数据结构的展开是递归的,这可能会导致意外的输出,但如果有循环引用的数据结构,也可能导致无限循环。请确保您的可迭代参数值合理,已经提醒过您
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('{}', [[0, [1, 2], 3]]) === '0, 1, 2, 3');
可选部分
另一个独特功能是可选部分,只有在标记的参数存在并且包含非空值(根据PHP的empty规则)时才会包含。可选部分通过将一些文本括在方括号内来指定
<?php use Fleshgrinder\Core\Formatter; $pattern = 'This is always printed[, and this is printed only if {this_argument}? is non-empty]'; assert( Formatter::format($pattern, ['this_argument' => '']) === 'This is always printed' ); assert( Formatter::format($pattern, ['this_argument' => 'this argument']) === 'This is always printed, and this is printed only if this argument is non-empty' );
占位符通过在占位符闭合括号的后面添加一个问号(?
)来标记。为了包含在可选部分中,所有标记的占位符必须非空。请注意,如果可选部分中的标记占位符缺失,则不会抛出异常,因为这被视为为空。此外,可选部分不能嵌套。如果您认为这会是一个更大的功能,请随时提出问题。
重要
只有固定位置和命名的占位符可以被标记,内部参数迭代器不考虑在内。这意味着实际上,以下模式从不包括可选部分,因为没有固定的位置或命名的占位符。
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('[{}?]', ['foobar']) === '');这个限制的原因很简单:可选部分在完整模式评估之前单独评估。因此,任何内部迭代器都会在可选部分内开始计数,这会导致难以理解且奇怪的行为;如果支持的话。
转义
特殊字符 [
、]
、{
和 }
可以通过加倍使用来转义,分别是 [[
、]]
、{{
或 }}
。
<?php use Fleshgrinder\Core\Formatter; assert(Formatter::format('[[{.1}, {.1}]]', [0, 1]) === '[0.0, 1.0]'); assert(Formatter::format('{:con}}junction}', [[0, 1]]) === '0 con}junction 1');
错误和异常
格式化过程中可能抛出的任何错误或异常都不会被捕获并向上传递。该方法本身会抛出已解释的 MissingPlaceholderException
。如果类型修饰符不存在且参数是
- 资源——因为它们没有有意义的格式化方式——或者
- 不是
Traversable
的对象,并且没有以下任何方法__toString
toFloat
toInt
toString
测试
打开终端,进入项目目录,执行以下命令以使用您本地安装的 PHP 执行程序运行 PHPUnit 测试。这要求您至少安装了 make
4.0
make
如果您系统上没有 make
或者版本太旧,也可以执行以下两个命令
composer install
composer test