news 2026/9/21 15:10:45

CodeIgniter Typography 排版库完全指南:从智能引号到语义化段落的自动排版

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CodeIgniter Typography 排版库完全指南:从智能引号到语义化段落的自动排版
  • 后端
  • Web框架

【免费下载链接】CodeIgniter

Open Source PHP Framework (originally from EllisLab)

项目地址:https://gitcode.com/gh_mirrors/co/CodeIgniter
点击查看免费下载

Typography 是 CodeIgniter 框架内置的文本格式化库,用于将普通纯文本自动转换为语义正确、排版规范的 HTML——包括自动识别段落、将引号转为弯引号实体、把连字符转为破折号、把三点省略号转为等。本文以 Typography 库官方文档 为主线,结合 系统源码 与 单元测试 深入讲解其初始化方式、全部公开方法与核心属性,帮助你把它用于博客评论、内容投稿、论坛帖子等需要"用户输入 → 干净 HTML"场景。

一、初始化 Typography 类

与 CodeIgniter 中大多数类库一致,Typography 库在控制器中通过$this->load->library()加载:

$this->load->library('typography');

加载完成后,类库实例通过$this->typography访问:

$this->typography->auto_typography($string);

底层实现位于 system/libraries/Typography.php,类名为CI_Typography,它没有继承其他框架核心类,是一个独立的自包含工具类。除了通过$this->load->library()加载外,框架还提供了对应的辅助函数封装(详见本文第五节),可以在不显式加载库的情况下直接调用。

二、类属性:控制排版行为的开关

CI_Typography暴露了若干public属性,其中文档明确记录的是$protect_braced_quotes,其余属性则从源码注释可以确认,它们共同构成了排版引擎的行为配置。

2.1$protect_braced_quotes:保护花括号内的引号

  • 类型:bool
  • 默认值:FALSE

当 Typography 库与 模板解析器 Parser 库 配合使用时,模板中的伪变量以花括号包裹,例如{blog_title}{parse="foobar"}。如果先对含花括号的模板字符串做排版、之后再交给 Parser 解析,花括号内引号被提前转成弯引号实体,会导致 Parser 的变量匹配失败。

protect_braced_quotes设为TRUE,即可让auto_typography()在排版时跳过花括号内的内容,原样保留其中的单双引号:

$this->load->library('typography'); $this->typography->protect_braced_quotes = TRUE;

该开关对应源码中的处理逻辑(Typography.php):当属性为TRUE时,花括号内的字符会先通过_protect_characters()替换为临时标记,排版结束后再还原:

if ($this->protect_braced_quotes === TRUE) { $str = preg_replace_callback('#\{.+?\}#si', array($this, '_protect_characters'), $str); }

单元测试 给出了开关开启与关闭的对比结果,非常直观:

$this->type->protect_braced_quotes = TRUE; $str = 'Test {parse="foobar"}'; // 结果: <p>Test {parse="foobar"}</p> —— 花括号内引号原样保留 $this->type->protect_braced_quotes = FALSE; $str = 'Test {parse="foobar"}'; // 结果: <p>Test {parse=&#8220;foobar&#8221;}</p> —— 引号被转成弯引号实体

可见关闭该开关时,"foobar"中的双引号会被转换为&#8220;/&#8221;,从而破坏 Parser 的{parse="foobar"}匹配。需要提醒的是:该保护只作用于"引号"这一类字符,花括号内其他排版规则(如段落包裹)是否生效取决于具体输入,使用前建议结合你的模板内容做一次实际验证。

2.2 其他排版相关的类属性(源码可见)

从 Typography.php 的类属性声明可以确认以下内部配置:

属性默认值作用
$block_elementsaddress\|blockquote\|div\|dl\|fieldset\|form\|h\d\|hr\|noscript\|object\|ol\|p\|pre\|script\|table\|ul不应被<p>包裹的块级元素列表
$skip_elementsp\|pre\|ol\|ul\|dl\|object\|table\|h\d内部不应再插入<p><br />的元素
$inline_elementsa\|abbr\|acronym\|b\|bdo\|big\|br\|button\|cite\|code\|del\|dfn\|em\|i\|img\|ins\|input\|label\|map\|kbd\|q\|samp\|select\|small\|span\|strong\|sub\|sup\|textarea\|tt\|var解析拆分字符串时应整体忽略的内联标签
$inner_block_requiredarray('blockquote')内部内容必须处于另一个块级元素内的元素
$last_block_element''最近一次解析到的块级元素名
$protect_braced_quotesFALSE是否保护花括号内的引号

这些属性是排版引擎区分"块级元素 / 跳过元素 / 内联元素"三类标签的依据:块级元素不会被包裹进<p>,但其包含的文本若含段落仍会被格式化;跳过元素内部不再插入<p><br /><pre>也因此天然得到保护);内联标签在拆分时作为整体保留,避免<img><a>等标签被错误拆分。

三、方法详解:auto_typography($str[, $reduce_linebreaks = FALSE])

这是 Typography 库的核心方法,接收任意字符串,返回"语义与排版均正确的 HTML"。

  • 参数:string $str输入字符串
  • 参数:bool $reduce_linebreaks是否将连续多余换行压缩为两个(默认FALSE
  • 返回:string排版安全的 HTML 字符串

基本用法:

$string = $this->typography->auto_typography($string);

3.1 官方文档列出的 7 大格式化规则

官方文档明确说明,auto_typography()会对输入做以下处理:

  1. 段落包裹:以"双换行"识别段落,将段落包裹进<p></p>
  2. 单换行转<br />:单个换行转换为<br />,但<pre>标签内的换行除外;
  3. 块级元素不被包裹<div>等块级元素本身不会套上<p>,但其内部文本若包含段落,仍会得到格式化;
  4. 弯引号转换:引号转换为方向正确的弯引号实体,标签内的引号除外;
  5. 弯撇号转换:撇号转换为弯撇号实体;
  6. 破折号转换:双连字符(无论是-- this还是like--this形式)转换为长破折号(em-dash,&#8212;);
  7. 省略号转换:单词前或后的连续三个句点转换为省略号(&#8230;);
  8. 句后双空格:句子后的双空格转换为不间断空格(&nbsp;),以模拟双空格效果。

3.2 可选参数:压缩连续换行

$reduce_linebreaks决定是否把超过两个的连续换行压缩到两个:

$string = $this->typography->auto_typography($string, TRUE);

源码中的对应实现(Typography.php):

if ($reduce_linebreaks === TRUE) { $str = preg_replace("/\n\n+/", "\n\n", $str); }

测试用例 验证了其效果:

$str = "This has way too many linebreaks.\n\n\n\nSee?"; // 结果: <p>This has way too many linebreaks.</p>\n\n<p>See?</p>

传入TRUE时,四个连续换行被压缩为两个,输出两个干净的<p>段落;此外在收尾清理阶段,TRUE模式还会移除空的<p>\n*</p>段落,而未开启时空段落会被填充为<p>&nbsp;</p>(Typography.php),确保浏览器能正确渲染为空段落而非被折叠。

3.3 源码级处理流水线

从源码看,auto_typography()的处理流程是一条精心设计的"保护-转换-还原"流水线(Typography.php):

  1. 空字符串短路$str === ''直接返回空串,避免后续正则开销;
  2. 换行标准化:把所有\r\n\r统一为\n,保证后续匹配一致性;
  3. (可选)压缩换行:见 3.2;
  4. 剥离 HTML 注释<!-- ... -->先被替换为{@HC0}{@HC1}等占位符,处理结束后再按原位恢复;
  5. 保护<pre>:整个<pre>...</pre>通过_protect_characters()整体占位,内部引号、双空格、双连字符全部换成临时标记,因此排版不会污染代码块;
  6. 保护标签内字符:对所有<.+?>标签调用_protect_characters(),将标签内的'"--、双空格分别替换为{@SQ}{@DQ}{@DD}{@NBS}
  7. (可选)保护花括号:当$protect_braced_quotesTRUE时对\{.+?\}执行同样的保护;
  8. 标记内联标签<开头且匹配$inline_elements的标签整体替换为{@TAG}...,避免在后续按标签拆串时被拆散;
  9. 按标签拆串:用preg_split()把字符串按 HTML 标签拆成数组(标签与文本交替),逐段循环处理;
  10. 块级元素识别:循环中命中$block_elements的片段原样保留;命中$skip_elements时切换$process标志,使<pre><ol>等内部文本跳过换行格式化;
  11. 换行格式化:对普通文本调用_format_newlines(),把\n\n替换为</p>\n\n<p>,把单个\n替换为<br />,最后整体包裹<p>...</p>
  12. 补开段落:若最终字符串不以块级元素开头,自动在最前补一个<p>
  13. 字符级格式化:调用format_characters()(见第四节)完成弯引号、破折号、省略号、不间断空格、&转义;
  14. 还原 HTML 注释与全部占位标记,并执行最终清理:去重连续</p>、清除块级元素前的游离<p></p>、修正></p>\n</p></的换行问题等。

其中_format_newlines()(Typography.php)还专门处理了$inner_block_required中列出的blockquote:当最近解析的块级元素是blockquote时,即使文本中没有换行也会强制进入换行处理逻辑,从而保证<blockquote>内部内容始终有块级结构包裹。

测试套件 将auto_typography()拆成 6 个子场景逐一验证:空字符串、换行标准化、换行压缩、HTML 注释保留、<pre>保护、缺少开标签时的自动补齐,以及花括号引号保护,覆盖了上述流水线的关键分支。

3.4 性能注意与缓存建议

官方文档特别提醒:排版格式化可能非常消耗处理器资源,尤其当你需要格式化大量内容时。auto_typography()内部包含多轮正则匹配与逐段循环,若在每次请求中都对长文本执行,会成为性能瓶颈。

因此官方建议:如果决定使用此方法,应考虑对页面做缓存处理。实践上推荐两种策略:

  • 对内容写入时排版、读取时直出:用户在投稿/评论提交时执行一次auto_typography(),将结果存入数据库,展示时不再重复排版;
  • 对动态页面使用框架的输出缓存(页面缓存或驱动缓存),缓存渲染后的 HTML。

四、方法详解:format_characters($str)

format_characters()auto_typography()类似,但只做字符级转换,不处理段落、换行与标签结构。

  • 参数:string $str输入字符串
  • 返回:string格式化后的字符串
$string = $this->typography->format_characters($string);

4.1 官方文档列出的 5 类转换

  1. 弯引号:引号转换为方向正确的弯引号实体,标签内的引号除外;
  2. 弯撇号:撇号转换为弯撇号实体;
  3. 长破折号:双连字符(-- thislike--this)转换为 em-dash;
  4. 省略号:单词前或后的三个连续句点转换为
  5. 句后双空格:句子后的双空格转换为不间断空格。

实现上,转换表是一个静态正则数组(Typography.php),只在首次调用时构建,后续调用直接复用,避免重复创建:

static $table; if ( ! isset($table)) { $table = array( // 嵌套智能引号(英文语法最多两层,单引号通常在外部) '/\'"(\s|$)/' => '&#8217;&#8221;$1', '/(^|\s|<p>)\'"/' => '$1&#8216;&#8220;', ... // 单引号智能引号 '/\'(\s|$)/' => '&#8217;$1', '/(^|\s|<p>)\'/' => '$1&#8216;', ... // 双引号智能引号 '/"(\s|$)/' => '&#8221;$1', '/(^|\s|<p>)"/' => '$1&#8220;', ... // 撇号 "/(\w)'(\w)/" => '$1&#8217;$2', // 破折号与省略号 '/\s?\-\-\s?/' => '&#8212;', '/(\w)\.{3}/' => '$1&#8230;', // 句子后的双空格 '/(\W) /' => '$1&nbsp; ', // 非实体 & 符号 '/&(?!#?[a-zA-Z0-9]{2,};)/' => '&amp;' ); } return preg_replace(array_keys($table), $table, $str);

引号方向判断的核心规则(源码注释明确说明):空白是决定引号弯向的主要因素,标点等非单词字符是空白之后的次要因素。因此"testing" in "theory"中的引号能正确识别为开/闭引号,而不是全部转成同一种方向的字符。

4.2 字符实体对照

format_characters()使用的实体及其对应字符如下:

输入输出实体含义
"(句首/空白前)&#8220;左双弯引号
"(句尾/空白后)&#8221;右双弯引号
'(词首)&#8216;左单弯引号
'(词尾/词中)&#8217;右单弯引号 / 撇号
--&#8212;em-dash 长破折号
...\w后三个句点)&#8230;省略号
句子后双空格&nbsp;不间断空格 + 空格
非实体&&amp;转义与符号

注意最后一条:&amp;&nbsp;已是合法实体的写法不会被二次转义。这一点在 测试用例 中有明确验证:

'&' => '&amp;', '&amp;' => '&amp;', // 已有实体不再重复转义 '&nbsp;'=> '&nbsp;', '--' => '&#8212;', 'foo...'=> 'foo&#8230;', 'foo..' => 'foo..', // 两个句点不转换 'test. new' => 'test.&nbsp; new',

4.3 与auto_typography()的关系

auto_typography()在完成段落与换行处理后,内部最后一步就是调用format_characters()(Typography.php):

$str = $this->format_characters($str);

因此可以这样理解两者的分工:format_characters()是"字符级排版引擎",auto_typography()是"结构级 + 字符级"的完整排版器。如果你的内容已经是结构完整的 HTML(例如由富文本编辑器生成),只想统一字符排版,可以直接使用format_characters()以避免段落结构被改写。

五、方法详解:nl2br_except_pre($str)

该方法将换行转换为<br />标签,但<pre>标签内的换行保持原样。

  • 参数:string $str输入字符串
  • 返回:string格式化后的字符串
$string = $this->typography->nl2br_except_pre($string);

它与 PHP 原生nl2br()行为一致,唯一区别是忽略<pre>标签。实现思路非常巧妙(Typography.php):按pre>字符串切分输入,偶数下标片段(pre开标签之前、</pre之后的部分)调用原生nl2br(),奇数下标片段(<pre></pre>之间的内容)原样保留:

public function nl2br_except_pre($str) { $newstr = ''; for ($ex = explode('pre>', $str), $ct = count($ex), $i = 0; $i < $ct; $i++) { $newstr .= (($i % 2) === 0) ? nl2br($ex[$i]) : $ex[$i]; if ($ct - 1 !== $i) { $newstr .= 'pre>'; } } return $newstr; }

测试用例 验证了行为:<pre>块内部的空行与换行全部原样保留,而块外的每个换行都被转换为<br />(包括</pre>之后紧接着的换行)。

六、辅助函数:无需显式加载库的快捷调用

除了通过$this->typography调用方法,框架还在 system/helpers/typography_helper.php 中提供了对应的辅助函数,加载方式:

$this->load->helper('typography');

6.1auto_typography($str[, $reduce_linebreaks = FALSE])

CI_Typography::auto_typography()的别名函数,内部自动加载 Typography 库并委托调用(typography_helper.php):

function auto_typography($str, $reduce_linebreaks = FALSE) { $CI =& get_instance(); $CI->load->library('typography'); return $CI->typography->auto_typography($str, $reduce_linebreaks); }

用法:

$string = auto_typography($string);

6.2nl2br_except_pre($str)

同样是对库方法的封装(typography_helper.php):

$string = nl2br_except_pre($string);

6.3entity_decode($str, $charset = NULL)

该文件还提供了一个相关函数entity_decode(),用于把 HTML 实体解码回字符。注意它并非 Typography 库的方法,而是CI_Security::entity_decode()的别名(typography_helper.php),详见安全库文档。由于auto_typography()/format_characters()会把引号等字符转成实体,entity_decode()常与之配套用于"排版 → 存储 → 反向还原"的场景。

七、综合实战:一个完整的排版流程示例

把本文介绍的能力组合起来,一个典型的"用户评论排版"流程如下:

<?php defined('BASEPATH') OR exit('No direct script access allowed'); class Comments extends CI_Controller { public function save() { $this->load->library('typography'); $this->load->helper('typography'); // 1. 从表单获取原始输入 $raw = $this->input->post('body'); // 2. 如需与模板解析器配合,先开启花括号引号保护 // $this->typography->protect_braced_quotes = TRUE; // 3. 执行完整排版(压缩多余换行) $formatted = $this->typography->auto_typography($raw, TRUE); // 或使用辅助函数等价写法: // $formatted = auto_typography($raw, TRUE); // 4. 存入数据库,展示时直接输出 $formatted $this->db->insert('comments', array('body' => $formatted)); } public function show() { // 读取已排版内容直接输出(不再重复排版,避免性能开销) $comment = $this->db->get_where('comments', array('id' => 1))->row(); echo $comment->body; } }

如果只想把换行转<br />而保留pre块原样(例如输出用户贴的代码片段),则:

$string = nl2br_except_pre($string);

八、总结与使用建议

Typography 库以三个公开方法覆盖了从"纯文本"到"排版 HTML"的完整链路:

  • auto_typography($str, $reduce_linebreaks):结构级 + 字符级完整排版,适合直接渲染用户内容;
  • format_characters($str):仅字符级转换,适合已有完整 HTML 结构的场景;
  • nl2br_except_pre($str):轻量换行转换,保留<pre>内容。

使用时的三条关键建议(均有文档或源码依据):

  1. 注意性能:官方文档明确提示auto_typography()属于处理器密集型操作,建议配合缓存或"写入时排版"策略;
  2. 保护模板变量:与 Parser 模板解析库 联用时,务必按需开启$protect_braced_quotes,否则花括号内的引号会被实体化导致变量无法匹配;
  3. 善用测试预期:Typography_test.php 中每个断言都可当作"输入 → 期望输出"的参考样例,在接入你自己的内容格式前,可先对照这些用例确认行为是否符合预期。
  • 后端
  • Web框架

【免费下载链接】CodeIgniter

Open Source PHP Framework (originally from EllisLab)

项目地址:https://gitcode.com/gh_mirrors/co/CodeIgniter
点击查看免费下载

相关推荐

上一篇:从8小时到30分钟:OpCore-Simplify如何颠覆Hackintosh配置体验
下一篇:CANN/GE外部分配器注销API

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/21 15:08:29

Java日期处理:获取N天前日期的最佳实践

1. 需求背景与场景解析在日常开发中&#xff0c;处理日期时间是最基础却最容易出错的环节之一。上周我就遇到一个典型场景&#xff1a;业务系统需要自动生成以"yyyyMMdd"格式命名的报表文件&#xff0c;但必须基于两周前的日期作为基准。类似这种"获取N天前日期…

作者头像 李华
网站建设 2026/9/21 15:06:43

半桥LLC软启动5大常见错误与解决方案

1. 半桥LLC软启动到底难在哪半桥LLC谐振变换器在中小功率电源里几乎是绕不开的拓扑&#xff0c;效率高、EMI友好、原副边隔离容易做&#xff0c;但凡做过300W以上适配器或者LED驱动的朋友&#xff0c;大概率都碰过它。可真正让工程师头疼的往往不是稳态效率&#xff0c;而是软启…

作者头像 李华
网站建设 2026/9/21 14:57:46

海康大华RTSP取流地址与播放方案实战指南:从URL格式到踩坑排查

前阵子给一条产线做视觉检测&#xff0c;现场混了十六路海康IPC、两台大华NVR&#xff0c;还有几个第三方球机要统一接入算法平台。头一天我以为半天能搞定&#xff0c;结果从下午两点死磕到晚上十一点&#xff0c;一半时间都浪费在“同一个RTSP标准协议&#xff0c;为什么地址…

作者头像 李华