fi1a/format

PHP 字符串模板格式化

2.2.2 2023-02-21 01:51 UTC

README

Latest Version Software License PHP Version Coverage Status Total Downloads Support mail

此包提供了使用指定符功能进行字符串模板格式化的能力。

包功能

  • 字符串和数字格式化;
  • 日期和时间格式化(月份和星期几);
  • 将特殊符号转换为HTML实体;
  • 将HTML实体转换回相应符号;
  • 内存大小格式化;
  • 时间格式化;
  • 数词后的词语变格;
  • 电话号码格式化;
  • 价格格式化;
  • 条件结构(if, elseif, else, endif);
  • 自定义指定符功能;
  • 可以为指定符功能设置缩写;
  • 可以将指定符功能链式应用于一个值。

安装

可以使用Composer将其作为依赖项安装。

composer require fi1a/format

使用

use Fi1a\Format\Formatter;

Formatter::format('[{{user:login}}] - {{user:name}}', ['user' => ['name' => 'John', 'login' => 'john85']]); // [john85] - John

Formatter::format('{{0}}, {{1}}',[1, 2]); // 1, 2

Formatter::format('{{}}, {{}}',[1, 2]); // 1, 2

Formatter::format('{{foo}}, {{foo}}, {{foo}}', ['foo' => 'bar',]); // bar, bar, bar

如果没有在值数组中找到键,则抛出异常 \Fi1a\Format\AST\Exception\NotFoundKey

use Fi1a\Format\Formatter;
use Fi1a\Format\AST\Exception\NotFoundKey;

try {
    Formatter::format('{{not_exists}}', []);
} catch (NotFoundKey $exception) {
    
}

所有值都通过 htmlspecialchars 函数转换为特殊HTML实体。要转换回,请使用指定符 unescape 或将第四个参数传递为 false 以取消自动将特殊符号转换为HTML实体的操作。

use Fi1a\Format\Formatter;

Formatter::format('{{value}}', ['value' => '&']); // "&"

Formatter::format('{{value|unescape}}', ['value' => '&']); // "&"
// или
Formatter::format('{{value}}', ['value' => '&'], [], false); // "&"

通过路径访问

字符串模板包含到关联数组中值的路径。

use Fi1a\Format\Formatter;

Formatter::format('[{{user:login}}] - {{user:name}}', ['user' => ['name' => 'John', 'login' => 'john85']]); // [john85] - John

通过索引访问

字符串模板包含数组中值的索引。

use Fi1a\Format\Formatter;

Formatter::format('{{0}}, {{1}}',[1, 2]); // 1, 2
Formatter::format('{{}}, {{}}',[3, 4]); // 3, 4

特殊符号转义

用于转义特殊符号的是反斜杠符号 "\"。

use Fi1a\Format\Formatter;

Formatter::format('\\{{0\\}}', [0 => 'foo']); // {{0}}
Formatter::format('{{0}}', [0 => 'foo']); // foo

要转义字符串中的所有特殊符号,可以使用 Fi1a\Format\Safe 类的 escape 函数。

use Fi1a\Format\Formatter;
use Fi1a\Format\Safe;

Formatter::format(Safe::escape(('{{0}}'), [0 => 'foo']); // {{0}}

要移除字符串中的转义特殊符号,可以使用 Fi1a\Format\Safe 类的 unescape 函数。

use Fi1a\Format\Formatter;
use Fi1a\Format\Safe;

Formatter::format(Safe::unescape(('\\{{0}}'), [0 => 'foo']); // foo

使用指定符功能

可用的指定符功能

  • date - 日期和时间的格式化;
  • sprintf - 字符串和数字的格式化;
  • escape - 将特殊符号转换为HTML实体;
  • unescape - 将特殊的HTML实体转换回相应符号;
  • memory - 内存大小的格式化(x.x B, x.x KB, x.x MB, x.x GB, x.x TB, x.x PB);
  • time - 时间的格式化(< 1 秒,x 秒,x 分钟,x 小时,x 天);
  • declension - 数词后的词语变格;
  • phone - 电话号码的格式化(+7(ddd)ddd-dddd);
  • price - 价格的格式化。

指定符函数的指示应跟在键之后,用 "|" 分隔。

use Fi1a\Format\Formatter;

Formatter::format('{{0|sprintf("04d")}}-{{1|sprintf("02d")}}-{{2|sprintf("02d")}}',[2016, 2, 27,]); // 2016-02-27

可以通过将动态指定符函数的修改器作为第三个参数传递给 format 函数来设置。

use Fi1a\Format\Formatter;

Formatter::format('{{value|sprintf(modifier)}}', ['value' => 100.5], ['modifier' => '01.2f']); // 100.50

可以使用 "|" 分隔指定符函数链式指定,这时值将按顺序逐个传递给各个指定符函数。

use Fi1a\Format\Formatter;

Formatter::format('{{value|unescape|sprintf|escape}}', ['value' => 'a&b']); // "a&amp;b"

使用 sprintf 格式化 PHP 字符串和数字

sprintf 函数的指定符。指定符函数的指示应跟在键之后,用 "|" 分隔。

字符串和数字的格式化。在 sprintf 函数中使用的指定符。

  • b - 将参数视为整数并以二进制表示打印。
  • c - 将参数视为整数并以相应的ASCII表中的字符打印。
  • d - 将参数视为整数并以带符号的整数打印。
  • e - 参数被认为是科学记数法中的数(即 1.2e+2)。精度指定符设置小数点后的数字位数。在较早的版本中,它指定了总的有效数字位数(即小数点后少一个符号)。
  • E - 与 e 指定符类似,但使用大写字母(即 1.2E+2)。
  • f - 参数被认为是带小数的数(考虑区域设置)。
  • F - 参数被认为是带小数的数(不考虑区域设置)。自 PHP 5.0.3 起可用。
  • o - 参数被视为整数,并以八进制形式打印。
  • s - 参数被视为字符串并打印为字符串。
  • u - 参数被视为整数,并以无符号整数形式打印。
  • x - 参数被视为整数,并以十六进制形式打印(字母将是小写)。
  • X - 参数被视为整数,并以十六进制形式打印(字母将是大写)。

使用填充字符

use Fi1a\Format\Formatter;

Formatter::format('{{0|sprintf("\'.9d")}}', [123]); // ......123
Formatter::format('{{0|sprintf("\'.09d")}}', [123]); // 000000123

带有前导零的整数

use Fi1a\Format\Formatter;

Formatter::format('{{0|sprintf("04d")}}-{{1|sprintf("02d")}}-{{2|sprintf("02d")}}', [2020, 6, 7]); // 2020-06-07

PHP 日期和时间格式化

date 函数的指定符,日期和时间格式化。指定函数指定符应跟在带有分隔符 "|" 的键之后。

函数中使用的格式化修饰符

  • d - 月份中的天,两位数字,带前导零(从 01 到 31)。
  • D - 俄语星期几的文本表示(从 Пн 到 Вс)。
  • j - 月份中的天,不带前导零(从 1 到 31)。
  • l - 俄语星期几的完整名称(从 Понедельник 到 Воскресенье)。
  • N - 根据 ISO 8601 标准的星期几的顺序号(从 1(星期一)到 7(星期日))。
  • S - 英语的序数词后缀,两个字符(st, nd, rd 或 th。与 j 一起使用)。
  • w - 星期几的顺序号(从 0(星期日)到 6(星期六))。
  • z - 年中的顺序号(从 0 到 365)。

  • W - 根据 ISO 8601 标准的年中的周顺序号;周从星期一开始,从 0 开始。例如:42(第 42 周)。

  • F - 俄语月份的完整名称,例如“Января”或“Марта”(从“Января”到“Декабря”)。
  • f - 俄语月份的完整名称,例如“Январь”或“Март”(从“Январь”到“Декабрь”)。
  • m - 月份的顺序号,带前导零(从 01 到 12)。
  • M - 俄语月份的缩写名称,3 个字符(从 “Янв” 到 “Дек”)。
  • n - 月份的顺序号,不带前导零(从 1 到 12)。
  • t - 指定月份中的天数(从 28 到 31)。

  • L - 闰年的标志(如果是闰年则为 1,否则为 0。)。
  • o - 根据 ISO 8601 标准的年号。它与 Y 有相同的值,除非 ISO 周号(W)属于前一年或后一年;在这种情况下,将使用该周的年份。(例如:1999 或 2003)。
  • X - 扩展的完整数字年份表示,至少 4 位数字,对于公元前年份用 - 表示,对于公元年份用 + 表示。(例如:-0055, +0787, +1999, +10191)。
  • x - 扩展的完整数字表示,如果需要则使用扩展的完整数字表示,如果可能则使用标准完整的数字表示(例如,Y)。至少 4 位数字。对于公元前年份使用前缀 -。对于公元前(包括)10000 年的年份使用前缀 +。(例如:-0055, 0787, 1999, +10191)。
  • Y - 完整的年份表示,至少 4 位数字,对于公元前年份用 - 表示。(例如:-0055, 0787, 1999, 2003, 10191。)
  • y - 年号,两位数字(例如:99, 03)。

时间

  • a - Ante meridiem(拉丁语“上午”)或 Post meridiem(拉丁语“下午”)的小写形式(am 或 pm)。
  • A - Ante meridiem 或 Post meridiem的大写形式(AM 或 PM)。
  • B - 互联网时间格式(另一种时间系统)(000 至 999)。
  • g - 12小时制时钟(不带前导零)(1 至 12)。
  • G - 24小时制时钟(不带前导零)(0 至 23)。
  • h - 12小时制时钟(带前导零)(01 至 12)。
  • H - 24小时制时钟(带前导零)(00 至 23)。
  • i - 带前导零的分钟(00 至 59)。
  • s - 带前导零的秒(00 至 59)。
  • u - 微秒。例如:654321。
  • v - 微秒。例如:654。
  • v - 微秒。例如:654。

时区

  • e - 时区标识符(例如:UTC,GMT,Atlantic/Azores)。
  • I - 夏令时标志(如果日期符合夏令时,则为 1,否则为 0)。
  • O - 与格林尼治时间的差值,小时和分钟之间没有冒号(例如:+0200)。
  • P - 与格林尼治时间的差值,小时和分钟之间有冒号(例如:+02:00)。
  • p - 与 P 相同,但将 +00:00 返回为 Z(从 PHP 8.0.0 开始可用)(例如:+02:00)。
  • T - 如果已知,则返回时区的缩写;否则返回格林尼治时间的偏移量。(例如:EST,MDT,+05)。
  • Z - 时区偏移量(秒)。对于位于 UTC 西部的时区,返回负数,而对于位于 UTC 东部的时区,返回正数。(从 -43200 到 50400)。

完整日期/时间

  • c - ISO 8601 格式的日期(2004-02-12T15:19:21+00:00)。
  • r - RFC 222/RFC 5322 格式的日期(例如:Thu, 21 Dec 2000 16:01:07 +0200)。
  • U - 自 Unix 纪元(1970 年 1 月 1 日 00:00:00 GMT)以来经过的秒数。(另请参阅 time())。

日期格式化键的俄语

  • F - 俄语月份的完整名称,例如“Января”或“Марта”(从“Января”到“Декабря”)。
  • f - 俄语月份的完整名称,例如“Январь”或“Март”(从“Январь”到“Декабрь”)。
  • M - 俄语月份的缩写名称,3 个字符(从 “Янв” 到 “Дек”)。
  • D - 俄语中星期几的文本表示(从“Пн”到“Вс”)。
  • l - 俄语中星期几的完整名称(从“Понедельник”到“Воскресенье”)。
use Fi1a\Format\Formatter;

Formatter::format('{{foo|date("d.m.Y")}}', ['foo' => time()]); // 28.09.2022
echo Formatter::format('{{|date("d F Y")}}', [time()]); // 18 Октября 2022
echo Formatter::format('{{|date("f")}}', [time()]); // Октябрь

设置默认格式

use Fi1a\Format\Specifier\Date;
use Fi1a\Format\Formatter;

Formatter::format('{{foo|date}}', ['foo' => time()]); // 28.09.2022 07:06:00
Date::setDefaultFormat('d.m.Y');
Formatter::format('{{foo|date}}', ['foo' => time()]); // 28.09.2022

函数转义指定符

将特殊符号转换为 HTML 实体 {{|escape(flags, encoding, doubleEncode)}}

use Fi1a\Format\Formatter;

Formatter::format('{{|escape}}', ['"test"']); // &quot;test&quot;

函数非转义指定符

将特殊的 HTML 实体转换回相应的符号 {{|escape(flags)}}

use Fi1a\Format\Formatter;

Formatter::format('{{|unescape}}', ['&amp;quot;test&amp;quot;']); // "test"

PHP 内存大小格式化

函数内存指定符。内存大小的格式化。

use Fi1a\Format\Formatter;

Formatter::format('{{|memory}}', [1024]); // 1.0 КБ
Formatter::format('{{|memory("B")}}', [1024]); // 1024.0 Б
Formatter::format('{{|memory}}', [1024 * 1024]); // 1.0 МБ

PHP 时间格式化

函数时间指定符,时间格式化。

函数指定符的可用参数

  • seconds - 格式化结果为秒;
  • minutes - 格式化结果为分钟;
  • hours - 格式化结果为小时;
  • days - 格式化结果为天。

如果指定符的参数未指定,则按最适合的尺寸进行格式化。

use Fi1a\Format\Formatter;

Formatter::format('{{|time}}', [60 * 60]); // 1 ч.
Formatter::format('{{|time("minutes")}}', [60 * 60]); // 60 мин.
Formatter::format('{{|time}}', [2 * 24 * 60 * 60]); // 2 д.

PHP 数词后名词的屈折

使用指定符函数 declension 可以对数字后的名词进行屈折。例如:1 год,2 года,5 лет。

use Fi1a\Format\Formatter;

Formatter::format('{{year}} {{year|declension("год", "года", "лет")}}', ['year' => 1]); // 1 год
Formatter::format('{{year}} {{year|declension("год", "года", "лет")}}', ['year' => 2]); // 2 года
Formatter::format('{{year}} {{year|declension("год", "года", "лет")}}', ['year' => 5]); // 5 лет

指定符屈折函数的修饰符

  • 第一个修饰符指定单数值的文本;
  • 第二个指定 2-4 个值的;
  • 第三个指定所有其他值。

电话号码格式化

使用指定符函数 phone 可以根据给定的掩码(格式)格式化电话号码。

use Fi1a\Format\Formatter;

Formatter::format('{{value|phone("+7(ddd)ddd-dddd")}}', ['value' => '+79228223576']); // +7(922)822-3576
Formatter::format('{{value|phone("+7(ddd)ddd-dddd")}}', ['value' => '9228223576']); // +7(922)822-3576
Formatter::format('{{value|phone("(dddd)dd-dd-dd")}}', ['value' => '(6783)44-00-44']); // (6783)44-00-44

数字格式化

使用指定符函数 number 可以格式化数字。

指定符数字格式化函数的修饰符

  • int decimals (2) - 小数点后的数字;
  • string decimalSeparator ('.') - 小数部分的分隔符;
  • string thousandsSeparator ('') - 千位分隔符;
  • bool allowZeroDecimal (false) - 允许在分数部分末尾输出 0。
use Fi1a\Format\Formatter;

Formatter::format('{{value|number}}', ['value' => 100.00]); // 100

Formatter::format(
    '{{value|number(decimals, decimalSeparator, thousandsSeparator, allowZeroDecimal)}}',
    ['value' => 100100.00],
    [
        'decimals' => 2,
        'decimalSeparator' => '.',
        'thousandsSeparator' => ' ',
        'allowZeroDecimal' => true,
    ]
); // 100 100.00

Formatter::format(
    '{{value|number(decimals, decimalSeparator, thousandsSeparator, allowZeroDecimal)}}',
    ['value' => 100100.00],
    [
        'decimals' => 2,
        'decimalSeparator' => '.',
        'thousandsSeparator' => ' ',
        'allowZeroDecimal' => false,
    ]
); // 100 100

Formatter::format(
    '{{value|number(decimals, decimalSeparator, thousandsSeparator, allowZeroDecimal)}}',
    ['value' => 100100.12],
    [
        'decimals' => 2,
        'decimalSeparator' => '.',
        'thousandsSeparator' => ' ',
        'allowZeroDecimal' => false,
    ]
); // 100 100.12

为了简化输入格式化数字的指定符函数,请使用缩写。

use Fi1a\Format\Formatter;

Formatter::addShortcut('number', 'number(2, ".", " ", false)');
Formatter::format('{{value|~number}}', ['value' => 100100.12]); // 100 100.12
Formatter::format('{{value|~number}}', ['value' => 100100]); // 100 100

PHP 格式化价格

通过使用指定符函数 price 可以格式化价格

价格格式化指定符函数的修饰符

  • int decimals (2) - 小数点后的数字;
  • string decimalSeparator ('.') - 小数部分的分隔符;
  • string thousandsSeparator ('') - 千位分隔符;
  • bool allowZeroDecimal (false) - 允许在分数部分末尾输出 0。
  • int round (null) - 四舍五入价格。传递常量:PHP_ROUND_HALF_UP - 当下一个数字在中间时向上取整,PHP_ROUND_HALF_DOWN - 当下一个数字在中间时向下取整。
  • int roundPrecision (0) - 进行四舍五入的十进制位数。
  • bool floor (false) - 向下取整(到最接近的整数)。
use Fi1a\Format\Formatter;

Formatter::format('{{value|price}} руб.', ['value' => 100.00]); // 100 руб.

Formatter::format(
    '{{value|price(decimals, decimalSeparator, thousandsSeparator, allowZeroDecimal)}} руб.',
    ['value' => 100100.00],
    [
        'decimals' => 2,
        'decimalSeparator' => '.',
        'thousandsSeparator' => ' ',
        'allowZeroDecimal' => true,
    ]
); // 100 100.00 руб.

Formatter::format(
    '{{value|price(decimals, decimalSeparator, thousandsSeparator, allowZeroDecimal)}} руб.',
    ['value' => 100100.00],
    [
        'decimals' => 2,
        'decimalSeparator' => '.',
        'thousandsSeparator' => ' ',
        'allowZeroDecimal' => false,
    ]
); // 100 100 руб.

Formatter::format(
    '{{value|price(decimals, decimalSeparator, thousandsSeparator, allowZeroDecimal)}} руб.',
    ['value' => 100100.12],
    [
        'decimals' => 2,
        'decimalSeparator' => '.',
        'thousandsSeparator' => ' ',
        'allowZeroDecimal' => false,
    ]
); // 100 100.12 руб.

Formatter::format(
    '{{value|price(decimals, decimalSeparator, thousandsSeparator, allowZeroDecimal, round, roundPrecision)}} руб.',
    ['value' => 100100.5],
    [
        'decimals' => 2,
        'decimalSeparator' => '.',
        'thousandsSeparator' => ' ',
        'allowZeroDecimal' => false,
        'round' => PHP_ROUND_HALF_UP,
        'roundPrecision' => 0,
    ]
); // 100 101 руб.

Formatter::format(
    '{{value|price(decimals, decimalSeparator, thousandsSeparator, allowZeroDecimal, round, roundPrecision)}} руб.',
    ['value' => 100100.5],
    [
        'decimals' => 2,
        'decimalSeparator' => '.',
        'thousandsSeparator' => ' ',
        'allowZeroDecimal' => false,
        'round' => PHP_ROUND_HALF_DOWN,
        'roundPrecision' => 0,
    ]
); // 100 100 руб.

Formatter::format(
    '{{value|price(decimals, decimalSeparator, thousandsSeparator, allowZeroDecimal, round, roundPrecision, floor)}} руб.',
    ['value' => 100100.6],
    [
        'decimals' => 0,
        'decimalSeparator' => '.',
        'thousandsSeparator' => ' ',
        'allowZeroDecimal' => false,
        'round' => null,
        'roundPrecision' => 0,
        'floor' => true,
    ]
); // 100 100 руб.

为了简化输入价格格式化指定符函数,请使用缩写。

use Fi1a\Format\Formatter;

Formatter::addShortcut('price', 'price(0, ".", " ", false, null, 0, true)');
Formatter::format('{{value|~price}} руб.', ['value' => 100100.6]); // 100 100 руб.

条件结构

以下条件结构可用:if, elseif, else, endif。在数组值中没有键时不会抛出异常。

use Fi1a\Format\Formatter;

Formatter::format('{{if(foo)}}{{bar}}{{else}}false{{endif}}', ['foo' => true, 'bar' => 'bar']); // bar

Formatter::format('{{if(not_exists)}}{{foo}}{{elseif(bar)}}{{bar}}{{endif}}', ['foo' => 'foo', 'bar' => 'bar']); // bar

在条件结构中可以使用指定符函数

use Fi1a\Format\Formatter;

Formatter::format('{{if(value|unescape==="a&b")}}{{value}}{{endif}}', ['value' => 'a&b']); // "a&amp;b"

添加指定符函数

指定符函数的类必须实现接口 \Fi1a\Format\Specifier\ISpecifier

通过方法 Fi1a\Format\Formatter::addSpecifier() 添加新的指定符函数。

use Fi1a\Format\Formatter;

Formatter::addSpecifier('spf', Specifier::class); // true

Formatter::format('{{foo|spf(true)}}', ['foo' => 'foo']); // foo

缩写

为了简化输入指定符函数,可以使用缩写。使用方法是通过 Formatter::addShortcut 方法添加缩写名称。要使用缩写,请从符号 "~" 开始输入名称。

use Fi1a\Format\Formatter;

Formatter::addShortcut('dt', 'date("d.m.Y", "en")');
Formatter::format('{{foo|~dt}}', ['foo' => time()]); // 29.09.2022