此包已被废弃,不再维护。未建议替代包。

字符串格式化库。

1.1.0 2017-03-16 21:21 UTC

This package is not auto-updated.

Last update: 2020-01-24 16:37:35 UTC


README

Latest Stable Version License Travis CI build status AppVeyor CI build status

Dependency Status Coveralls branch Scrutinizer Code Climate: GPA Total Downloads

格式

format库提供格式特殊字符串模式的功能。其实现类似于 sprintfmsgfmt_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