Files
typecho/var/Utils/AutoP.php
joyqi 3caebb3b20 v1.3.0 (#1661)
* Add feed widget

* add feed render

* Add CommentPage widget

* New theme (#1390)

* 调整忽略目录

* add theme

* fix theme scss build

Co-authored-by: fen <f3nb0x@gmail.com>

* s/is_writeable/is_writable/g

* New upgrade method

* merge new fixes from master

* add pgsql ssl mode support (ref #1600) (#1623)

* Feat/code refactor (#1626)

* remove all magic methods, add type for class properties

* refactor codes

* fix all

* refactor code

* fix type

* fix all

* fix request is method

* fix all

* fix router

* fix get page

* fix 1.3.0 upgrade

* [feat] support high resolution avatar

* fix types in i18n component

* Implement Ctrl+S or Command+S for save draft (#1628)

* Implement Ctrl+S or Command+S for save draft

* rename

* add Typecho.savePost

* fix upload file size

* add new uploader

* replace new uploader

* fix textarea change

* fix preview

* refactor post edit

* fix issue

* fix page edit

---------

Co-authored-by: joyqi <joyqi@segmentfault.com>
Co-authored-by: joyqi <magike.net@gmail.com>

* fix #1632

* Add svg to image types

* Feat/tree pages (#1646)

* add tree trait

* finish category tree trait

* support select fields

* fix select fields

* refactor admin trait

* fix draft status

* Add new contents type "revision"

* minor refactor

* add more tree view abstracts

* add tree trait to pages

* get ready for tree view pages

* improve page edit

* fix revision

* fix slug

* add router params delegate

* fix params delegate

* fix

* fix

* fix all

* fix all

* fix tree

* fix page link

* fix feed

* fix page

* fix permalink

* fix permalink input

* fix offset query

* Support IDN (#1629)

* Support IDN

* use js

* Optimize code

* Optimize code

* fix URL script

* remove unnecessary use

---------

Co-authored-by: joyqi <joyqi@segmentfault.com>

* fix input element

* fix #1651, close #1653

* Use json instead of serialize (#1624)

* Use json instead of serialize

* Fix Upgrade code

* add tree trait

* finish category tree trait

* support select fields

* fix select fields

* refactor admin trait

* fix draft status

* Add new contents type "revision"

* minor refactor

* add more tree view abstracts

* add tree trait to pages

* get ready for tree view pages

* improve page edit

* fix revision

* fix slug

* add router params delegate

* fix params delegate

* fix

* fix

* fix all

* fix all

* fix tree

* fix page link

* fix feed

* fix page

* fix permalink

* fix permalink input

* fix offset query

* Fix typo

* remove proxy methods

* remove unnecessary useage

---------

Co-authored-by: joyqi <joyqi@segmentfault.com>
Co-authored-by: joyqi <magike.net@gmail.com>

* Fix Prevent XSS vulnerability in default theme (#1654)

* Fix Prevent XSS vulnerability in default theme

* Update var/Typecho/Db/Adapter/Pdo.php

* fix the getter

---------

Co-authored-by: joyqi <joyqi@segmentfault.com>

* add throwCallback to widget response

* fix: cut down fields when selecting recent posts

* fix typo errors

* fix typo errors

* fix http client cookie

* add throw finish

* fix theme lang

* fix default theme

* fix query

* add open graph and twitter card support
add canonical link

* fix canonical link meta

* fix theme classic-22

* remove unnecessary scss file when packaging

* init plugin signal

* improve: remove feather-icon js file

* fix: typo

* improve: post detail layout

* fix tags saving

* improve: nav search

* fix: theme screenshot

* fix: theme page layout

* remove php 7.2/7.3 env

---------

Co-authored-by: fen <f3nb0x@gmail.com>
Co-authored-by: Lu Fei <52o@qq52o.cn>
2023-12-30 23:02:25 +08:00

205 lines
6.0 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace Utils;
/**
* AutoP
*
* @copyright Copyright (c) 2012 Typecho Team. (http://typecho.org)
* @author Joyqi <magike.net@gmail.com>
* @license GNU General Public License 2.0
*/
class AutoP
{
// 作为段落的标签
private const BLOCK = 'p|pre|div|blockquote|form|ul|ol|dd|table|ins|h1|h2|h3|h4|h5|h6';
/**
* 唯一id
*
* @access private
* @var integer
*/
private int $uniqueId = 0;
/**
* 存储的段落
*
* @access private
* @var array
*/
private array $blocks = [];
/**
* 替换段落的回调函数
*
* @param array $matches 匹配值
* @return string
*/
public function replaceBlockCallback(array $matches): string
{
$tagMatch = '|' . $matches[1] . '|';
$text = $matches[4];
switch (true) {
/** 用br处理换行 */
case false !== strpos(
'|li|dd|dt|td|p|a|span|cite|strong|sup|sub|small|del|u|i|b|ins|h1|h2|h3|h4|h5|h6|',
$tagMatch
):
$text = nl2br(trim($text));
break;
/** 用段落处理换行 */
case false !== strpos('|div|blockquote|form|', $tagMatch):
$text = $this->cutByBlock($text);
if (false !== strpos($text, '</p><p>')) {
$text = $this->fixParagraph($text);
}
break;
default:
break;
}
/** 没有段落能力的标签 */
if (false !== strpos('|a|span|font|code|cite|strong|sup|sub|small|del|u|i|b|', $tagMatch)) {
$key = '<b' . $matches[2] . '/>';
} else {
$key = '<p' . $matches[2] . '/>';
}
$this->blocks[$key] = "<{$matches[1]}{$matches[3]}>{$text}</{$matches[1]}>";
return $key;
}
/**
* 用段落方法处理换行
*
* @param string $text
* @return string
*/
private function cutByBlock(string $text): string
{
$space = "( | )";
$text = str_replace("\r\n", "\n", trim($text));
$text = preg_replace("/{$space}*\n{$space}*/is", "\n", $text);
$text = preg_replace("/\s*<p:([0-9]{4})\/>\s*/is", "</p><p:\\1/><p>", $text);
$text = preg_replace("/\n{2,}/", "</p><p>", $text);
$text = nl2br($text);
$text = preg_replace("/(<p>)?\s*<p:([0-9]{4})\/>\s*(<\/p>)?/is", "<p:\\2/>", $text);
$text = preg_replace("/<p>{$space}*<\/p>/is", '', $text);
$text = preg_replace("/\s*<p>\s*$/is", '', $text);
$text = preg_replace("/^\s*<\/p>\s*/is", '', $text);
return $text;
}
/**
* 修复段落开头和结尾
*
* @param string $text
* @return string
*/
private function fixParagraph(string $text): string
{
$text = trim($text);
if (!preg_match("/^<(" . self::BLOCK . ")(\s|>)/i", $text)) {
$text = '<p>' . $text;
}
if (!preg_match("/<\/(" . self::BLOCK . ")>$/i", $text)) {
$text = $text . '</p>';
}
return $text;
}
/**
* 自动分段
*
* @param string $text
* @return string
*/
public function parse(string $text): string
{
/** 重置计数器 */
$this->uniqueId = 0;
$this->blocks = [];
/** 将已有的段落后面的换行处理掉 */
$text = preg_replace(["/<\/p>\s+<p(\s*)/is", "/\s*<br\s*\/?>\s*/is"], ["</p><p\\1", "<br />"], trim($text));
/** 将所有非自闭合标签解析为唯一的字符串 */
$foundTagCount = 0;
$textLength = strlen($text);
$uniqueIdList = [];
if (preg_match_all("/<\/\s*([a-z0-9]+)>/is", $text, $matches, PREG_OFFSET_CAPTURE)) {
foreach ($matches[0] as $key => $match) {
$tag = $matches[1][$key][0];
$leftOffset = $match[1] - $textLength;
$posSingle = strrpos($text, '<' . $tag . '>', $leftOffset);
$posFix = strrpos($text, '<' . $tag . ' ', $leftOffset);
$pos = false;
switch (true) {
case (false !== $posSingle && false !== $posFix):
$pos = max($posSingle, $posFix);
break;
case false === $posSingle && false !== $posFix:
$pos = $posFix;
break;
case false !== $posSingle && false === $posFix:
$pos = $posSingle;
break;
default:
break;
}
if (false !== $pos) {
$uniqueId = $this->makeUniqueId();
$uniqueIdList[$uniqueId] = $tag;
$tagLength = strlen($tag);
$text = substr_replace($text, $uniqueId, $pos + 1 + $tagLength, 0);
$text = substr_replace(
$text,
$uniqueId,
$match[1] + 7 + $foundTagCount * 10 + $tagLength,
0
); // 7 = 5 + 2
$foundTagCount++;
}
}
}
foreach ($uniqueIdList as $uniqueId => $tag) {
$text = preg_replace_callback(
"/<({$tag})({$uniqueId})([^>]*)>(.*)<\/\\1\\2>/is",
[$this, 'replaceBlockCallback'],
$text,
1
);
}
$text = $this->cutByBlock($text);
$blocks = array_reverse($this->blocks);
foreach ($blocks as $blockKey => $blockValue) {
$text = str_replace($blockKey, $blockValue, $text);
}
return $this->fixParagraph($text);
}
/**
* 生成唯一的id, 为了速度考虑最多支持1万个tag的处理
*
* @return string
*/
private function makeUniqueId(): string
{
return ':' . str_pad($this->uniqueId ++, 4, '0', STR_PAD_LEFT);
}
}