2017-11-30

This commit is contained in:
Visman 2017-11-30 10:08:32 +07:00
parent d1dbeb272a
commit a4a81c04e0
24 changed files with 310 additions and 1151 deletions

66
app/Core/Parser.php Normal file
View file

@ -0,0 +1,66 @@
<?php
namespace ForkBB\Core;
use Parserus;
class Parser extends Parserus
{
/**
* Контейнер
* @var Container
*/
protected $c;
/**
* Конструктор
*
* @param int $flag
* @param Container $container
*/
public function __construct($flag, Container $container)
{
$this->c = $container;
parent::__construct($flag);
$this->init();
}
/**
* Инициализация данных
*/
protected function init()
{
$bbcodes = include $this->c->DIR_CONFIG . '/defaultBBCode.php';
$this->setBBCodes($bbcodes);
if ($this->c->user->show_smilies == '1'
&& ($this->c->config->o_smilies_sig == '1' || $this->c->config->o_smilies == '1')
) {
$smilies = $this->c->smilies->list; //????
foreach ($smilies as &$cur) {
$cur = $this->c->PUBLIC_URL . '/img/sm/' . $cur;
}
unset($cur);
$info = $this->c->BBCODE_INFO;
$this->setSmilies($smilies)->setSmTpl($info['smTpl'], $info['smTplTag'], $info['smTplBl']);
}
}
/**
* Метод добавляет один bb-код
*
* @param array $bb
*
* @return Parser
*/
public function addBBCode(array $bb)
{
if ($bb['tag'] == 'quote') {
$bb['self nesting'] = (int) $this->c->config->o_quote_depth;
}
return parent::addBBCode($bb);
}
}

View file

@ -100,7 +100,7 @@ class LoadTree extends MethodModel
foreach ($list as $forum) {
$t = max($max, (int) $forum->mf_mark_all_read);
if ($forum->last_post > $t) {
$time[$id] = $t;
$time[$forum->id] = $t;
}
}

View file

@ -391,7 +391,6 @@ class Topic extends Page
$this->topic = $topic;
$this->posts = $posts;
$this->crumbs = $this->crumbs($topic);
$this->pagination = $topic->pagination;
$this->online = $this->c->Online->calc($this)->info();
$this->stats = null;
$this->form = $form;
@ -399,6 +398,7 @@ class Topic extends Page
if ($topic->showViews) {
$topic->incViews();
}
$topic->updateVisits();
/*
if (! $user->isGuest) {
$vars = [

View file

@ -112,4 +112,26 @@ class Post extends DataModel
}
return $controls;
}
/**
* HTML код сообщения
*
* @return string
*/
public function html()
{
$bbWList = $this->c->config->p_message_bbcode == '1' ? null : [];
$bbBList = $this->c->config->p_message_img_tag == '1' ? [] : ['img'];
$parser = $this->c->Parser->setAttr('isSign', false)
->setWhiteList($bbWList)
->setBlackList($bbBList)
->parse($this->cens()->message);
if ($this->hide_smilies != '1' && $this->c->config->o_smilies == '1') {
$parser->detectSmilies();
}
return $parser->getHtml();
}
}

View file

@ -330,6 +330,7 @@ class Topic extends DataModel
$cur = $this->c->ModelPost->setAttrs($cur);
}
unset($cur);
$this->timeMax = $timeMax;
return $posts;
}
@ -355,4 +356,48 @@ class Topic extends DataModel
$this->c->DB->query($sql, $vars);
}
/**
* Обновление меток последнего визита и последнего прочитанного сообщения
*/
public function updateVisits()
{
if ($this->c->user->isGuest) {
return;
}
$vars = [
':uid' => $this->c->user->id,
':tid' => $this->id,
':read' => $this->mt_last_read,
':visit' => $this->mt_last_visit,
];
$flag = false;
if (false !== $this->hasNew) {
$flag = true;
$vars[':visit'] = $this->last_post;
}
if (false !== $this->hasUnread && $this->timeMax > $this->hasUnread) {
$flag = true;
$vars[':read'] = $this->timeMax;
}
if ($flag) {
if (empty($this->mt_last_read) && empty($this->mt_last_visit)) {
$sql = 'INSERT INTO ::mark_of_topic (uid, tid, mt_last_visit, mt_last_read)
SELECT ?i:uid, ?i:tid, ?i:visit, ?i:read
FROM ::groups
WHERE NOT EXISTS (SELECT 1
FROM ::mark_of_topic
WHERE uid=?i:uid AND tid=?i:tid)
LIMIT 1';
} else {
$sql = 'UPDATE ::mark_of_topic
SET mt_last_visit=?i:visit, mt_last_read=?i:read
WHERE uid=?i:uid AND tid=?i:tid';
}
$this->c->DB->exec($sql, $vars);
}
}
}

View file

@ -186,4 +186,26 @@ class User extends DataModel
{
return isset($this->c->Online->online[$this->id]);
}
/**
* HTML код подписи
*
* @return string
*/
protected function gethtmlSign()
{
$bbWList = $this->c->config->p_sig_bbcode == '1' ? $this->c->BBCODE_INFO['forSign'] : [];
$bbBList = $this->c->config->p_sig_img_tag == '1' ? [] : ['img'];
$parser = $this->c->Parser->setAttr('isSign', true)
->setWhiteList($bbWList)
->setBlackList($bbBList)
->parse($this->cens()->signature);
if ($this->c->config->o_smilies_sig == '1') {
$parser->detectSmilies();
}
return $parser->getHtml();
}
}

View file

@ -4,10 +4,8 @@ return [
['tag' => 'ROOT',
'type' => 'block',
'handler' => function($body) {
$body = '<p>' . $body . '</p>';
// Replace any breaks next to paragraphs so our replace below catches them
$body = preg_replace('%(</?p>)(?:\s*?<br>){1,2}%', '$1', $body);
$body = preg_replace('%(</?p>)(?:\s*?<br>){1,2}%', '$1', '<p>' . $body . '</p>');
$body = preg_replace('%(?:<br>\s*?){1,2}(</?p>)%', '$1', $body);
// Remove any empty paragraph tags (inserted via quotes/lists/code/etc) which should be stripped
@ -32,9 +30,7 @@ return [
'no attr' => true,
],
'handler' => function($body, $attrs) {
$body = trim($body, "\n\r");
$class = substr_count($body, "\n") > 28 ? ' class="vscroll"' : '';
return '</p><div class="codebox"><pre' . $class . '><code>' . $body . '</code></pre></div><p>';
return '</p><div class="codebox"><pre><code>' . trim($body, "\n\r") . '</code></pre></div><p>';
},
],
['tag' => 'b',
@ -86,7 +82,7 @@ return [
},
],
['tag' => 'color',
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'Def' => [
'format' => '%^(?:\#(?:[\dA-Fa-f]{3}){1,2}|(?:aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|yellow|white))$%',
@ -97,7 +93,7 @@ return [
},
],
['tag' => 'colour',
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'Def' => [
'format' => '%^(?:\#(?:[\dA-Fa-f]{3}){1,2}|(?:aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|yellow|white))$%',
@ -108,7 +104,7 @@ return [
},
],
['tag' => 'background',
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'Def' => [
'format' => '%^(?:\#(?:[\dA-Fa-f]{3}){1,2}|(?:aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|yellow|white))$%',
@ -119,7 +115,7 @@ return [
},
],
['tag' => 'size',
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'Def' => [
'format' => '%^[1-9]\d*(?:em|ex|pt|px|\%)?$%',
@ -156,7 +152,7 @@ return [
},
],
['tag' => 'font',
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'Def' => [
'format' => '%^[a-z\d, -]+$%i',
@ -187,7 +183,7 @@ return [
],
['tag' => '*',
'type' => 'block',
'self nesting' => true,
'self nesting' => 5,
'parents' => ['list'],
'auto' => true,
'handler' => function($body) {
@ -196,7 +192,7 @@ return [
],
['tag' => 'list',
'type' => 'list',
'self nesting' => true,
'self nesting' => 5,
'tags only' => true,
'attrs' => [
'Def' => true,
@ -251,7 +247,7 @@ return [
],
['tag' => 'quote',
'type' => 'block',
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'Def' => true,
'no attr' => true,
@ -268,7 +264,7 @@ return [
],
['tag' => 'spoiler',
'type' => 'block',
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'Def' => true,
'no attr' => true,
@ -354,7 +350,7 @@ return [
}
if ($url === $body) {
$url = htmlspecialchars_decode($url, ENT_QUOTES);
$url = htmlspecialchars_decode($url, ENT_QUOTES | ENT_HTML5);
$url = mb_strlen($url, 'UTF-8') > 55 ? mb_substr($url, 0, 39, 'UTF-8') . ' … ' . mb_substr($url, -10, null, 'UTF-8') : $url;
$body = $parser->e($url);
}
@ -365,7 +361,7 @@ return [
['tag' => 'table',
'type' => 'table',
'tags only' => true,
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'no attr' => true,
'style' => true,
@ -390,7 +386,7 @@ return [
['tag' => 'caption',
'type' => 'block',
'parents' => ['table'],
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'no attr' => true,
'style' => true,
@ -407,7 +403,7 @@ return [
'type' => 't',
'parents' => ['table'],
'tags only' => true,
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'no attr' => true,
'style' => true,
@ -424,7 +420,7 @@ return [
'type' => 't',
'parents' => ['table'],
'tags only' => true,
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'no attr' => true,
'style' => true,
@ -441,7 +437,7 @@ return [
'type' => 't',
'parents' => ['table'],
'tags only' => true,
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'no attr' => true,
'style' => true,
@ -458,7 +454,7 @@ return [
'type' => 'tr',
'parents' => ['table', 't'],
'tags only' => true,
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'no attr' => true,
'style' => true,
@ -474,7 +470,7 @@ return [
['tag' => 'th',
'type' => 'block',
'parents' => ['tr'],
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'no attr' => true,
'style' => true,
@ -492,7 +488,7 @@ return [
['tag' => 'td',
'type' => 'block',
'parents' => ['tr'],
'self nesting' => true,
'self nesting' => 5,
'attrs' => [
'no attr' => true,
'style' => true,

View file

@ -22,7 +22,7 @@
@endsection
@section('pagination')
<nav class="f-pages">
@foreach($p->pagination as $cur)
@foreach($p->topic->pagination as $cur)
@if($cur[2])
<span class="f-page active">{{ $cur[1] }}</span>
@elseif($cur[1] === 'space')
@ -40,7 +40,7 @@
@extends('layouts/main')
<div class="f-nav-links">
@yield('crumbs')
@if($p->topic->canReply || $p->topic->closed || $p->pagination)
@if($p->topic->canReply || $p->topic->closed || $p->topic->pagination)
<div class="f-links-b clearfix">
@yield('pagination')
@yield('linkpost')
@ -70,7 +70,7 @@
@else
<li class="f-username">{{ $post->user->username }}</li>
@endif
@if($post->showUserAvatar)
@if($post->showUserAvatar && $post->user->avatar)
<li class="f-avatar">
<img alt="{{ $post->user->username }}" src="{!! $post->user->avatar !!}">
</li>
@ -91,12 +91,12 @@
@endif
</address>
<div class="f-post-right f-post-main">
{!! $post->message !!}
{!! $post->html() !!}
</div>
@if($post->showSignature && $post->user->signature)
<div class="f-post-right f-post-signature">
<hr>
{!! $post->user->signature !!}
{!! $post->user->htmlSign !!}
</div>
@endif
</div>
@ -118,7 +118,7 @@
@endforeach
</section>
<div class="f-nav-links">
@if($p->topic->canReply || $p->topic->closed || $p->pagination)
@if($p->topic->canReply || $p->topic->closed || $p->topic->pagination)
<div class="f-links-a clearfix">
@yield('linkpost')
@yield('pagination')

17
composer.lock generated
View file

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "f222f403c8d48f00cb5e3e37f680ebcb",
"content-hash": "b8c1ffae094a89502ad8a3c60385d4b8",
"hash": "683b977706502db4bb7fc7ba3cd8ca71",
"content-hash": "2299621b481bdbc8ce629eee53b1b82e",
"packages": [
{
"name": "artoodetoo/dirk",
@ -55,16 +55,16 @@
},
{
"name": "miovisman/parserus",
"version": "0.9.1",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/MioVisman/Parserus.git",
"reference": "d039178d46c989ebb8eb89970b52eb6ce927c817"
"reference": "bbdade91cfc4927ebb8b5b984f90a52075f75ece"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MioVisman/Parserus/zipball/d039178d46c989ebb8eb89970b52eb6ce927c817",
"reference": "d039178d46c989ebb8eb89970b52eb6ce927c817",
"url": "https://api.github.com/repos/MioVisman/Parserus/zipball/bbdade91cfc4927ebb8b5b984f90a52075f75ece",
"reference": "bbdade91cfc4927ebb8b5b984f90a52075f75ece",
"shasum": ""
},
"require": {
@ -93,14 +93,15 @@
"bbcode",
"parser"
],
"time": "2016-11-26 06:56:51"
"time": "2017-11-21 06:34:31"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"artoodetoo/dirk": 20
"artoodetoo/dirk": 20,
"miovisman/parserus": 20
},
"prefer-stable": false,
"prefer-lowest": false,

View file

@ -49,23 +49,23 @@
},
{
"name": "miovisman/parserus",
"version": "0.9.1",
"version_normalized": "0.9.1.0",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/MioVisman/Parserus.git",
"reference": "d039178d46c989ebb8eb89970b52eb6ce927c817"
"reference": "bbdade91cfc4927ebb8b5b984f90a52075f75ece"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MioVisman/Parserus/zipball/d039178d46c989ebb8eb89970b52eb6ce927c817",
"reference": "d039178d46c989ebb8eb89970b52eb6ce927c817",
"url": "https://api.github.com/repos/MioVisman/Parserus/zipball/bbdade91cfc4927ebb8b5b984f90a52075f75ece",
"reference": "bbdade91cfc4927ebb8b5b984f90a52075f75ece",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"time": "2016-11-26 06:56:51",
"time": "2017-11-21 06:34:31",
"type": "library",
"installation-source": "dist",
"autoload": {

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2016 Visman
Copyright (c) 2016-2017 Visman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -1,7 +1,7 @@
<?php
/**
* @copyright Copyright (c) 2016 Visman. All rights reserved.
* @copyright Copyright (c) 2016-2017 Visman. All rights reserved.
* @author Visman <mio.visman@yandex.ru>
* @link https://github.com/MioVisman/Parserus
* @license https://opensource.org/licenses/MIT The MIT License (MIT)
@ -119,6 +119,12 @@ class Parserus
*/
protected $strict = false;
/**
* Максимальная глубина дерева тегов при строгом режиме поиска ошибок
* @var int
*/
protected $maxDepth;
/**
* Конструктор
*
@ -131,14 +137,15 @@ class Parserus
}
$this->eFlags = $flag | ENT_QUOTES | ENT_SUBSTITUTE;
$this->tRepl = in_array($flag, [ENT_HTML5, ENT_HTML401])
? ['<br>', '&nbsp; &nbsp; ', '&nbsp; ', ' &nbsp;']
: ['<br />', '&#160; &#160; ', '&#160; ', ' &#160;'];
? ['<br>', '&nbsp; &nbsp; ', '&nbsp; ', ' &nbsp;']
: ['<br />', '&#160; &#160; ', '&#160; ', ' &#160;'];
}
/**
* Метод добавляет один bb-код
*
* @param array $bb Массив описания bb-кода
*
* @return Parserus $this
*/
public function addBBCode(array $bb)
@ -174,7 +181,7 @@ class Parserus
}
if (isset($bb['self nesting'])) {
$res['self nesting'] = (bool) $bb['self nesting'];
$res['self nesting'] = (int) $bb['self nesting'] > 0 ? (int) $bb['self nesting'] : false;
}
if (isset($bb['recursive'])) {
@ -248,6 +255,7 @@ class Parserus
* Метод задает массив bb-кодов
*
* @param array $bbcodes Массив описаний bb-кодов
*
* @return Parserus $this
*/
public function setBBCodes(array $bbcodes)
@ -276,6 +284,7 @@ class Parserus
* Метод задает массив смайлов
*
* @param array $smilies Ассоциативный массив смайлов
*
* @return Parserus $this
*/
public function setSmilies(array $smilies)
@ -331,6 +340,7 @@ class Parserus
* @param string $tpl Строка шаблона, например: <img src="{url}" alt="{alt}">
* @param string $tag Имя тега под которым идет отображение смайлов
* @param array $bl Список тегов в которых не нужно отображать смайлы
*
* @return Parserus $this
*/
public function setSmTpl($tpl, $tag = 'img', array $bl = ['url'])
@ -356,6 +366,7 @@ class Parserus
* Метод устанавливает список разрешенных bb-кодов
*
* @param mixed $list Массив bb-кодов, null и т.д.
*
* @return Parserus $this
*/
public function setWhiteList($list = null)
@ -368,6 +379,7 @@ class Parserus
* Метод устанавливает список запрещенных bb-кодов
*
* @param mixed $list Массив bb-кодов, null и т.д.
*
* @return Parserus $this
*/
public function setBlackList($list = null)
@ -381,6 +393,7 @@ class Parserus
*
* @param string $name Имя переменной
* @param mixed $val Значение переменной
*
* @return Parserus $this
*/
public function setAttr($name, $val)
@ -392,8 +405,9 @@ class Parserus
/**
* Метод для получения значения переменной
*
* @param string $name Имя переменной
* @return mixed|null Значение переменной или null, если переменная не была задана ранее
* @param string $name Имя переменной
*
* @return mixed Значение переменной или null, если переменная не была задана ранее
*/
public function attr($name)
{
@ -407,15 +421,16 @@ class Parserus
* @param int $parentId Указатель на родителя
* @param array $attrs Массив атрибутов тега
* @param bool $textOnly Флаг. Если true, то в теле только текст
*
* @return int Указатель на данный тег
*/
protected function addTagNode($tag, $parentId = null, $attrs = [], $textOnly = false)
protected function addTagNode($tag, $parentId = null, array $attrs = [], $textOnly = false)
{
$this->data[++$this->dataId] = [
'tag' => $tag,
'parent' => $parentId,
'tag' => $tag,
'parent' => $parentId,
'children' => [],
'attrs' => $attrs,
'attrs' => $attrs,
];
if ($textOnly) {
@ -434,14 +449,14 @@ class Parserus
*
* @param string $text Текст
* @param int $parentId Указатель на родителя
*
* @return string Пустая строка
*/
protected function addTextNode($text, $parentId)
{
if (isset($text[0])) {
$this->data[++$this->dataId] = [
'text' => $text,
'text' => $text,
'parent' => $parentId,
];
@ -455,6 +470,7 @@ class Parserus
* Метод нормализует содержимое атрибута
*
* @param string $attr Содержимое атрибута полученное из регулярного выражения
*
* @return string
*/
protected function getNormAttr($attr)
@ -476,6 +492,7 @@ class Parserus
* @param string $tag Имя обрабатываемого тега
* @param string $type "Тип атрибутов" = ' ', '=' или ']'
* @param string $text Текст из которого выделяются атрибуты
*
* @return null|array
*/
protected function parseAttrs($tag, $type, $text)
@ -575,8 +592,8 @@ class Parserus
return [
'attrs' => $attrs,
'tag' => $tagText,
'text' => $text,
'tag' => $tagText,
'text' => $text,
];
}
@ -584,6 +601,7 @@ class Parserus
* Метод определяет указатель на родительский тег для текущего
*
* @param string $tag Имя тега
*
* @return int|false false, если невозможно подобрать родителя
*/
protected function findParent($tag)
@ -629,9 +647,10 @@ class Parserus
* @param string $tag Имя тега
* @param array $attrs Массив атрибутов
* @param string $text Текст из которого выделяется тело тега
*
* @return array|false false в случае ошибки
*/
protected function validationTag($tag, $attrs, $text)
protected function validationTag($tag, array $attrs, $text)
{
if (empty($attrs)) {
$attrs['no attr'] = null;
@ -691,8 +710,8 @@ class Parserus
return [
'attrs' => $attrs,
'body' => $flag ? $body : null,
'end' => $end,
'body' => $flag ? $body : null,
'end' => $end,
];
}
@ -702,6 +721,7 @@ class Parserus
* @param string $tag Имя обрабатываемого тега
* @param string $curText Текст до тега, который еще не был учтен
* @param string $tagText Текст самого тега - [/tag]
*
* @return string Пустая строка, если тег удалось закрыть
*/
protected function closeTag($tag, $curText, $tagText) {
@ -715,7 +735,9 @@ class Parserus
$curTag = $this->data[$curId]['tag'];
while ($curTag !== $tag && $curId > 0) {
if (false === $this->bbcodes[$curTag]['auto']) {
if ($this->bbcodes[$tag]['type'] === 'inline'
|| false === $this->bbcodes[$curTag]['auto']
) {
break;
}
@ -753,6 +775,7 @@ class Parserus
$this->smOn = false;
$this->errors = [];
$this->strict = isset($opts['strict']) ? (bool) $opts['strict'] : false;
$this->maxDepth = isset($opts['depth']) ? (int) $opts['depth'] : 10;
}
/**
@ -760,9 +783,10 @@ class Parserus
*
* @param string $text Обрабатываемый текст
* @param array $opts Ассоциативный массив опций
*
* @return Parserus $this
*/
public function parse($text, $opts = [])
public function parse($text, array $opts = [])
{
$this->reset($opts);
$curText = '';
@ -774,13 +798,13 @@ class Parserus
while (($match = preg_split('%(\[(/)?(' . ($recCount ? $recTag : '[a-z\*][a-z\d-]{0,10}') . ')((?(1)\]|[=\]\x20])))%i', $text, 2, PREG_SPLIT_DELIM_CAPTURE))
&& isset($match[1])
) {
/* $match[0] - текст до тега
* $match[1] - [ + (|/) + имя тега + (]| |=)
* $match[2] - (|/)
* $match[3] - имя тега
* $match[4] - тип атрибутов --> (]| |=)
* $match[5] - остаток текста до конца
*/
/* $match[0] - текст до тега
* $match[1] - [ + (|/) + имя тега + (]| |=)
* $match[2] - (|/)
* $match[3] - имя тега
* $match[4] - тип атрибутов --> (]| |=)
* $match[5] - остаток текста до конца
*/
$tagText = $match[1];
$curText .= $match[0];
$text = $match[5];
@ -869,13 +893,62 @@ class Parserus
}
$this->addTextNode($curText . $text, $this->curId);
if ($this->strict) {
$this->searchError();
}
return $this;
}
/**
* Метод проверяет глубину дерева тегов
* Метод проверяет лимит вложенности тегов в самих себя
*
* @param int $id Указатель на текущий тег
* @param int $depth Глубина дерева на текущий момент
* @param array $tags Массив количества вложений тегов с включенным 'self nesting'
*
* @return bool
*/
protected function searchError($id = 0, $depth = -1, array $tags = [])
{
if (isset($this->data[$id]['text'])) {
return false;
}
++$depth;
if ($depth > $this->maxDepth) {
$this->errors[] = [15, $this->maxDepth];
return true;
}
$tag = $this->data[$id]['tag'];
if (false !== $this->bbcodes[$tag]['self nesting']) {
if (isset($tags[$tag])) {
++$tags[$tag];
} else {
$tags[$tag] = 0;
}
if ($tags[$tag] > $this->bbcodes[$tag]['self nesting']) {
$this->errors[] = [16, $tag, $this->bbcodes[$tag]['self nesting']];
return true;
}
}
foreach ($this->data[$id]['children'] as $child) {
if ($this->searchError($child, $depth, $tags)) {
return true;
}
}
return false;
}
/**
* Метод возвращает HTML построенный на основании дерева тегов
*
* @param int $id Указатель на текущий тег
*
* @return string
*/
public function getHtml($id = 0)
@ -952,6 +1025,7 @@ class Parserus
* Метод возвращает текст с bb-кодами построенный на основании дерева тегов
*
* @param int $id Указатель на текущий тег
*
* @return string
*/
public function getCode($id = 0)
@ -1021,6 +1095,7 @@ class Parserus
* @param string $tag Имя для создания bb-кода
* @param string $pattern Регулярное выражение для поиска
* @param bool $textOnly Флаг. true, если содержимое созданного тега текстовое
*
* @return Parserus $this
*/
protected function detect($tag, $pattern, $textOnly)
@ -1073,7 +1148,7 @@ class Parserus
$pos = $match[1] + strlen($match[0]);
}
$this->addTextNode($this->endStr($this->data[$id]['text'], $pos), $pid);
$this->addTextNode((string) substr($this->data[$id]['text'], $pos), $pid);
unset($this->data[$id]);
$this->data[$pid]['children'] = array_merge($this->data[$pid]['children'], $arrEnd);
@ -1112,6 +1187,7 @@ class Parserus
*
* @param string $mask Маска символов, которые не учитываются при определении пустоты текстовых узлов
* @param int $id Указатель на текущий тег
*
* @return bool Если true, то тег/узел пустой
*/
protected function stripEmptyTags_($mask, $id)
@ -1155,10 +1231,12 @@ class Parserus
/**
* Метод возвращает массив ошибок
*
* @param array $lang Массив строк шаблонов описания ошибок
* @param array $lang Массив строк шаблонов описания ошибок
* @param array $errors Массив, который дополняется ошибками
*
* @return array
*/
public function getErrors(array $lang = [])
public function getErrors(array $lang = [], array $errors = [])
{
$defLang = [
1 => 'Тег [%1$s] находится в черном списке',
@ -1174,11 +1252,11 @@ class Parserus
11 => 'Тело тега [%1$s] не соответствует шаблону',
12 => 'Тег [%1$s] нельзя открыть внутри аналогичного тега',
13 => 'В теге [%1$s] отсутствует обязательный атрибут \'%2$s\'',
14 => 'Все теги пустые'
14 => 'Все теги пустые',
15 => 'Глубина дерева тегов больше %1$s',
16 => 'Тег [%1$s] вложен в себя больше %2$s раз',
];
$errors = [];
foreach ($this->errors as $args) {
$err = array_shift($args);
@ -1200,23 +1278,11 @@ class Parserus
* Метод преобразует специальные символы в HTML-сущности
*
* @param string $text
*
* @return string
*/
public function e($text)
{
return htmlspecialchars($text, $this->eFlags, 'UTF-8');
}
/**
* Метод возвращает окончание строки
*
* @param string $str Текст
* @param int $pos Начальная позиция в байтах с которой идет возврат текста
* @return string
*/
protected function endStr($str, $pos)
{
$s = substr($str, $pos);
return false === $s ? '' : $s;
}
}

View file

@ -1,20 +0,0 @@
<?php
include '../Parserus.php';
$parser = new Parserus();
echo $parser->addBBCode([
'tag' => 'b',
'handler' => function($body) {
return '<b>' . $body . '</b>';
}
])->addBBcode([
'tag' => 'i',
'handler' => function($body) {
return '<i>' . $body . '</i>';
},
])->parse("[i]Hello\n[b]World[/b]![/i]")
->getHTML();
#output: <i>Hello<br><b>World</b>!</i>

View file

@ -1,20 +0,0 @@
<?php
include '../Parserus.php';
$parser = new Parserus(ENT_XHTML);
echo $parser->addBBCode([
'tag' => 'b',
'handler' => function($body) {
return '<b>' . $body . '</b>';
}
])->addBBcode([
'tag' => 'i',
'handler' => function($body) {
return '<i>' . $body . '</i>';
},
])->parse("[i]Hello\n[b]World[/b]![/i]")
->getHTML();
#output: <i>Hello<br /><b>World</b>!</i>

View file

@ -1,20 +0,0 @@
<?php
include '../Parserus.php';
$parser = new Parserus();
echo $parser->addBBCode([
'tag' => 'b',
'handler' => function($body) {
return '<b>' . $body . '</b>';
}
])->addBBcode([
'tag' => 'i',
'handler' => function($body) {
return '<i>' . $body . '</i>';
},
])->parse("[i]\nHello\n[b]\nWorld!")
->getHTML();
#output: <i>Hello<br><b>World!</b></i>

View file

@ -1,50 +0,0 @@
<?php
include '../Parserus.php';
$parser = new Parserus();
echo $parser->addBBCode([
'tag' => 'after',
'type' => 'block',
'single' => true,
'attrs' => [
'Def' => [
'format' => '%^\d+$%',
],
],
'handler' => function($body, $attrs, $parser) {
$lang = $parser->attr('lang');
$arr = array();
$sec = $attrs['Def'] % 60;
$min = ($attrs['Def'] / 60) % 60;
$hours = ($attrs['Def'] / 3600) % 24;
$days = (int) ($attrs['Def'] / 86400);
if ($days > 0) {
$arr[] = $days . $lang['After time d'];
}
if ($hours > 0) {
$arr[] = $hours . $lang['After time H'];
}
if ($min > 0) {
$arr[] = (($min < 10) ? '0' . $min : $min) . $lang['After time i'];
}
if ($sec > 0) {
$arr[] = (($sec < 10) ? '0' . $sec : $sec) . $lang['After time s'];
}
$attr = $lang['After time'] . ' ' . implode(' ', $arr);
return '<span style="color: #808080"><em>' . $attr . ':</em></span><br>';
},
])->setAttr('lang', [
'After time' => 'Added later',
'After time s' => ' s',
'After time i' => ' min',
'After time H' => ' h',
'After time d' => ' d',
])->parse('[after=10123]')
->getHTML();
#output: <span style="color: #808080"><em>Added later 2 h 48 min 43 s:</em></span><br>

View file

@ -1,588 +0,0 @@
<?php
include '../Parserus.php';
# example on the basis of (partially) bb-codes from FluxBB/PunBB parsers
# пример на основании (частично) bb-кодов из парсеров FluxBB/PunBB
$bbcodes = [
['tag' => 'ROOT',
'type' => 'block',
'handler' => function($body) {
$body = '<p>' . $body . '</p>';
// Replace any breaks next to paragraphs so our replace below catches them
$body = preg_replace('%(</?p>)(?:\s*?<br />){1,2}%', '$1', $body);
$body = preg_replace('%(?:<br />\s*?){1,2}(</?p>)%', '$1', $body);
// Remove any empty paragraph tags (inserted via quotes/lists/code/etc) which should be stripped
$body = str_replace('<p></p>', '', $body);
$body = preg_replace('%<br />\s*?<br />%', '</p><p>', $body);
$body = str_replace('<p><br />', '<br /><p>', $body);
$body = str_replace('<br /></p>', '</p><br />', $body);
$body = str_replace('<p></p>', '<br /><br />', $body);
return $body;
},
],
['tag' => 'code',
'type' => 'block',
'recursive' => true,
'text only' => true,
'pre' => true,
'attrs' => [
'Def' => true,
'no attr' => true,
],
'handler' => function($body, $attrs) {
$body = trim($body, "\n\r");
$class = substr_count($body, "\n") > 28 ? ' class="vscroll"' : '';
return '</p><div class="codebox"><pre' . $class . '><code>' . $body . '</code></pre></div><p>';
},
],
['tag' => 'b',
'handler' => function($body) {
return '<strong>' . $body . '</strong>';
},
],
['tag' => 'i',
'handler' => function($body) {
return '<em>' . $body . '</em>';
},
],
['tag' => 'em',
'handler' => function($body) {
return '<em>' . $body . '</em>';
},
],
['tag' => 'u',
'handler' => function($body) {
return '<span class="bbu">' . $body . '</span>';
},
],
['tag' => 's',
'handler' => function($body) {
return '<span class="bbs">' . $body . '</span>';
},
],
['tag' => 'del',
'handler' => function($body) {
return '<del>' . $body . '</del>';
},
],
['tag' => 'ins',
'handler' => function($body) {
return '<ins>' . $body . '</ins>';
},
],
['tag' => 'h',
'type' => 'h',
'handler' => function($body) {
return '</p><h5>' . $body . '</h5><p>';
},
],
['tag' => 'hr',
'type' => 'block',
'single' => true,
'handler' => function() {
return '</p><hr /><p>';
},
],
['tag' => 'color',
'self nesting' => true,
'attrs' => [
'Def' => [
'format' => '%^(?:\#(?:[\dA-Fa-f]{3}){1,2}|(?:aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|yellow|white))$%',
],
],
'handler' => function($body, $attrs) {
return '<span style="color:' . $attrs['Def'] . ';">' . $body . '</span>';
},
],
['tag' => 'colour',
'self nesting' => true,
'attrs' => [
'Def' => [
'format' => '%^(?:\#(?:[\dA-Fa-f]{3}){1,2}|(?:aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|yellow|white))$%',
],
],
'handler' => function($body, $attrs) {
return '<span style="color:' . $attrs['Def'] . ';">' . $body . '</span>';
},
],
['tag' => 'background',
'self nesting' => true,
'attrs' => [
'Def' => [
'format' => '%^(?:\#(?:[\dA-Fa-f]{3}){1,2}|(?:aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|yellow|white))$%',
],
],
'handler' => function($body, $attrs) {
return '<span style="background-color:' . $attrs['Def'] . ';">' . $body . '</span>';
},
],
['tag' => 'size',
'self nesting' => true,
'attrs' => [
'Def' => [
'format' => '%^[1-9]\d*(?:em|ex|pt|px|\%)?$%',
],
],
'handler' => function($body, $attrs) {
if (is_numeric($attrs['Def'])) {
$attrs['Def'] .= 'px';
}
return '<span style="font-size:' . $attrs['Def'] . ';">' . $body . '</span>';
},
],
['tag' => 'right',
'type' => 'block',
'handler' => function($body) {
return '</p><p style="text-align: right;">' . $body . '</p><p>';
},
],
['tag' => 'center',
'type' => 'block',
'handler' => function($body) {
return '</p><p style="text-align: center;">' . $body . '</p><p>';
},
],
['tag' => 'justify',
'type' => 'block',
'handler' => function($body) {
return '</p><p style="text-align: justify;">' . $body . '</p><p>';
},
],
['tag' => 'mono',
'handler' => function($body) {
return '<code>' . $body . '</code>';
},
],
['tag' => 'font',
'self nesting' => true,
'attrs' => [
'Def' => [
'format' => '%^[a-z\d, -]+$%i',
],
],
'handler' => function($body, $attrs) {
return '<span style="font-family:' . $attrs['Def'] . ';">' . $body . '</span>';
},
],
['tag' => 'email',
'type' => 'email',
'attrs' => [
'Def' => [
'format' => '%^[^\x00-\x1f\s]+?@[^\x00-\x1f\s]+$%',
],
'no attr' => [
'body format' => '%^[^\x00-\x1f\s]+?@[^\x00-\x1f\s]+$%D',
'text only' => true,
],
],
'handler' => function($body, $attrs) {
if (empty($attrs['Def'])) {
return '<a href="mailto:' . $body . '">' . $body . '</a>';
} else {
return '<a href="mailto:' . $attrs['Def'] . '">' . $body . '</a>';
}
},
],
['tag' => '*',
'type' => 'block',
'self nesting' => true,
'parents' => ['list'],
'auto' => true,
'handler' => function($body) {
return '<li><p>' . $body . '</p></li>';
},
],
['tag' => 'list',
'type' => 'list',
'self nesting' => true,
'tags only' => true,
'attrs' => [
'Def' => true,
'no attr' => true,
],
'handler' => function($body, $attrs) {
if (!isset($attrs['Def'])) {
$attrs['Def'] = '*';
}
switch ($attrs['Def'][0]) {
case 'a':
return '</p><ol class="alpha">' . $body . '</ol><p>';
case '1':
return '</p><ol class="decimal">' . $body . '</ol><p>';
default:
return '</p><ul>' . $body . '</ul><p>';
}
},
],
['tag' => 'after',
'type' => 'block',
'single' => true,
'attrs' => [
'Def' => [
'format' => '%^\d+$%',
],
],
'handler' => function($body, $attrs, $parser) {
$lang = $parser->attr('lang');
$arr = array();
$sec = $attrs['Def'] % 60;
$min = ($attrs['Def'] / 60) % 60;
$hours = ($attrs['Def'] / 3600) % 24;
$days = (int) ($attrs['Def'] / 86400);
if ($days > 0) {
$arr[] = $days . $lang['After time d'];
}
if ($hours > 0) {
$arr[] = $hours . $lang['After time H'];
}
if ($min > 0) {
$arr[] = (($min < 10) ? '0' . $min : $min) . $lang['After time i'];
}
if ($sec > 0) {
$arr[] = (($sec < 10) ? '0' . $sec : $sec) . $lang['After time s'];
}
$attr = $lang['After time'] . ' ' . implode(' ', $arr);
return '<span style="color: #808080"><em>' . $attr . ':</em></span><br />';
},
],
['tag' => 'quote',
'type' => 'block',
'self nesting' => true,
'attrs' => [
'Def' => true,
'no attr' => true,
],
'handler' => function($body, $attrs, $parser) {
if (isset($attrs['Def'])) {
$lang = $parser->attr('lang');
$st = '</p><div class="quotebox"><cite>' . $attrs['Def'] . ' ' . $lang['wrote'] . '</cite><blockquote><div><p>';
} else {
$st = '</p><div class="quotebox"><blockquote><div><p>';
}
return $st . $body . '</p></div></blockquote></div><p>';
},
],
['tag' => 'spoiler',
'type' => 'block',
'self nesting' => true,
'attrs' => [
'Def' => true,
'no attr' => true,
],
'handler' => function($body, $attrs, $parser) {
if (isset($attrs['Def'])) {
$st = '</p><div class="quotebox" style="padding: 0px;"><div onclick="var e,d,c=this.parentNode,a=c.getElementsByTagName(\'div\')[1],b=this.getElementsByTagName(\'span\')[0];if(a.style.display!=\'\'){while(c.parentNode&&(!d||!e||d==e)){e=d;d=(window.getComputedStyle?getComputedStyle(c, null):c.currentStyle)[\'backgroundColor\'];if(d==\'transparent\'||d==\'rgba(0, 0, 0, 0)\')d=e;c=c.parentNode;}a.style.display=\'\';a.style.backgroundColor=d;b.innerHTML=\'&#9650;\';}else{a.style.display=\'none\';b.innerHTML=\'&#9660;\';}" style="font-weight: bold; cursor: pointer; font-size: 0.9em;"><span style="padding: 0 5px;">&#9660;</span>' . $attrs['Def'] . '</div><div style="padding: 6px; margin: 0; display: none;"><p>';
} else {
$lang = $parser->attr('lang');
$st = '</p><div class="quotebox" style="padding: 0px;"><div onclick="var e,d,c=this.parentNode,a=c.getElementsByTagName(\'div\')[1],b=this.getElementsByTagName(\'span\')[0];if(a.style.display!=\'\'){while(c.parentNode&&(!d||!e||d==e)){e=d;d=(window.getComputedStyle?getComputedStyle(c, null):c.currentStyle)[\'backgroundColor\'];if(d==\'transparent\'||d==\'rgba(0, 0, 0, 0)\')d=e;c=c.parentNode;}a.style.display=\'\';a.style.backgroundColor=d;b.innerHTML=\'&#9650;\';}else{a.style.display=\'none\';b.innerHTML=\'&#9660;\';}" style="font-weight: bold; cursor: pointer; font-size: 0.9em;"><span style="padding: 0 5px;">&#9660;</span>' . $lang['Hidden text'] . '</div><div style="padding: 6px; margin: 0; display: none;"><p>';
}
return $st . $body . '</p></div></div><p>';
},
],
['tag' => 'img',
'type' => 'img',
'parents' => ['inline', 'block', 'url'],
'text only' => true,
'attrs' => [
'Def' => [
'body format' => '%^(?:(?:ht|f)tps?://[^\x00-\x1f\s<"]+|data:image/[a-z]+;base64,(?:[a-zA-Z\d/\+\=]+))$%D'
],
'no attr' => [
'body format' => '%^(?:(?:ht|f)tps?://[^\x00-\x1f\s<"]+|data:image/[a-z]+;base64,(?:[a-zA-Z\d/\+\=]+))$%D'
],
],
'handler' => function($body, $attrs, $parser) {
if (! isset($attrs['Def'])) {
$attrs['Def'] = (substr($body, 0, 11) === 'data:image/') ? 'base64' : basename($body);
}
// тег в подписи
if ($parser->attr('isSign')) {
if ($parser->attr('showImgSign')) {
return '<img src="' . $body . '" alt="' . $attrs['Def'] . '" class="sigimage" />';
}
} else {
// тег в теле сообщения
if ($parser->attr('showImg')) {
return '<span class="postimg"><img src="' . $body . '" alt="' . $attrs['Def'] . '" /></span>';
}
}
$lang = $parser->attr('lang');
return '<a href="' . $body . '" rel="nofollow">&lt;' . $lang['Image link']. ' - ' . $attrs['Def'] . '&gt;</a>';
},
],
['tag' => 'url',
'type' => 'url',
'parents' => ['inline', 'block'],
'attrs' => [
'Def' => [
'format' => '%^[^\x00-\x1f]+$%',
],
'no attr' => [
'body format' => '%^[^\x00-\x1f]+$%D',
],
],
'handler' => function($body, $attrs, $parser) {
if (isset($attrs['Def'])) {
$url = $attrs['Def'];
} else {
$url = $body;
// возможно внутри была картинка, которая отображается как ссылка
if (preg_match('%^<a href=".++(?<=</a>)$%D', $url)) {
return $url;
}
// возможно внутри картинка
if (preg_match('%<img src="([^"]+)"%', $url, $match)) {
$url = $match[1];
}
}
$fUrl = str_replace(array(' ', '\'', '`', '"'), array('%20', '', '', ''), $url);
if (strpos($url, 'www.') === 0) {
$fUrl = 'http://'.$fUrl;
} else if (strpos($url, 'ftp.') === 0) {
$fUrl = 'ftp://'.$fUrl;
} else if (strpos($url, '/') === 0) {
$fUrl = $parser->attr('baseUrl') . $fUrl;
} else if (!preg_match('%^([a-z0-9]{3,6})://%', $url)) {
$fUrl = 'http://'.$fUrl;
}
if ($url === $body) {
$url = htmlspecialchars_decode($url, ENT_QUOTES);
$url = mb_strlen($url, 'UTF-8') > 55 ? mb_substr($url, 0, 39, 'UTF-8') . ' … ' . mb_substr($url, -10, null, 'UTF-8') : $url;
$body = $parser->e($url);
}
return '<a href="' . $fUrl . '" rel="nofollow">' . $body . '</a>';
},
],
['tag' => 'table',
'type' => 'table',
'tags only' => true,
'self nesting' => true,
'attrs' => [
'no attr' => true,
'style' => true,
'align' => true,
'background' => true,
'bgcolor' => true,
'border' => true,
'bordercolor' => true,
'cellpadding' => true,
'cellspacing' => true,
'frame' => true,
'rules' => true,
],
'handler' => function($body, $attrs) {
$attr = '';
foreach ($attrs as $key => $val) {
$attr .= ' ' . $key . '="' . $val . '"';
}
return '</p><table' . $attr . '>' . $body . '</table><p>';
},
],
['tag' => 'caption',
'type' => 'block',
'parents' => ['table'],
'self nesting' => true,
'attrs' => [
'no attr' => true,
'style' => true,
],
'handler' => function($body, $attrs) {
$attr = '';
foreach ($attrs as $key => $val) {
$attr .= ' ' . $key . '="' . $val . '"';
}
return '<caption' . $attr . '><p>' . $body . '</p></caption>';
},
],
['tag' => 'thead',
'type' => 't',
'parents' => ['table'],
'tags only' => true,
'self nesting' => true,
'attrs' => [
'no attr' => true,
'style' => true,
],
'handler' => function($body, $attrs) {
$attr = '';
foreach ($attrs as $key => $val) {
$attr .= ' ' . $key . '="' . $val . '"';
}
return '<thead' . $attr . '>' . $body . '</thead>';
},
],
['tag' => 'tbody',
'type' => 't',
'parents' => ['table'],
'tags only' => true,
'self nesting' => true,
'attrs' => [
'no attr' => true,
'style' => true,
],
'handler' => function($body, $attrs) {
$attr = '';
foreach ($attrs as $key => $val) {
$attr .= ' ' . $key . '="' . $val . '"';
}
return '<tbody' . $attr . '>' . $body . '</tbody>';
},
],
['tag' => 'tfoot',
'type' => 't',
'parents' => ['table'],
'tags only' => true,
'self nesting' => true,
'attrs' => [
'no attr' => true,
'style' => true,
],
'handler' => function($body, $attrs) {
$attr = '';
foreach ($attrs as $key => $val) {
$attr .= ' ' . $key . '="' . $val . '"';
}
return '<tfoot' . $attr . '>' . $body . '</tfoot>';
},
],
['tag' => 'tr',
'type' => 'tr',
'parents' => ['table', 't'],
'tags only' => true,
'self nesting' => true,
'attrs' => [
'no attr' => true,
'style' => true,
],
'handler' => function($body, $attrs) {
$attr = '';
foreach ($attrs as $key => $val) {
$attr .= ' ' . $key . '="' . $val . '"';
}
return '<tr' . $attr . '>' . $body . '</tr>';
},
],
['tag' => 'th',
'type' => 'block',
'parents' => ['tr'],
'self nesting' => true,
'attrs' => [
'no attr' => true,
'style' => true,
'colspan' => true,
'rowspan' => true,
],
'handler' => function($body, $attrs) {
$attr = '';
foreach ($attrs as $key => $val) {
$attr .= ' ' . $key . '="' . $val . '"';
}
return '<th' . $attr . '><p>' . $body . '</p></th>';
},
],
['tag' => 'td',
'type' => 'block',
'parents' => ['tr'],
'self nesting' => true,
'attrs' => [
'no attr' => true,
'style' => true,
'colspan' => true,
'rowspan' => true,
],
'handler' => function($body, $attrs) {
$attr = '';
foreach ($attrs as $key => $val) {
$attr .= ' ' . $key . '="' . $val . '"';
}
return '<td' . $attr . '><p>' . $body . '</p></td>';
},
],
];
$lang = [
'Hidden text' => 'Hidden text',
'wrote' => 'wrote:', // For [quote]'s
'After time' => 'Added later',
'After time s' => ' s',
'After time i' => ' min',
'After time H' => ' h',
'After time d' => ' d',
'Image link' => 'image', // This is displayed (i.e. <image>) instead of images when "Show images" is disabled in the profile
];
$text = '[table align="center" border="1" bordercolor="#ccc" cellpadding="5" cellspacing="0" style="border-collapse:collapse; width:500px"]
[caption][b]Table[/b][/caption]
[thead]
[tr]
[th style=width:50%]Column 1[/th]
[th style=width:50%]Column 2[/th]
[/tr]
[/thead]
[tbody]
[tr]
[td]1.1[/td]
[td]
[list]
[*]1.2
[*]1.3
[*]1.4
[/list]
[/td]
[/tr]
[/tbody]
[/table]
[size=36]Hello World![/size]
[img]data:image/gif;base64,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[/img]
[code]
$parser->setBBCodes($bbcodes)
->setAttr(\'baseUrl\', \'http://localhost\')
->setAttr(\'lang\', $lang)
->setAttr(\'showImg\', true)
->setAttr(\'showImgSign\', true)
->setAttr(\'isSign\', false)
->parse($text);
[/code]
[center][background=#00CCCC][url=https://github.com/MioVisman/Parserus]Parserus[/url] BBCode parser[/background][/center]
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
';
$parser = new Parserus(ENT_XHTML);
$parser->setBBCodes($bbcodes)
->setAttr('baseUrl', 'http://localhost')
->setAttr('lang', $lang)
->setAttr('showImg', true)
->setAttr('showImgSign', true)
->setAttr('isSign', false)
->parse($text);
echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>' .
$parser->getHtml() .
'</body>
</html>';

View file

@ -1,27 +0,0 @@
<?php
include '../Parserus.php';
$parser = new Parserus();
echo $parser->setBBCodes([
['tag' => 'url',
'type' => 'url',
'parents' => ['inline', 'block'],
'attrs' => [
'Def' => [
'format' => '%^[^\x00-\x1f]+$%',
],
'no attr' => [
'body format' => '%^[^\x00-\x1f]+$%D',
],
],
'handler' => function($body, $attrs, $parser) {
#...
},
],
])->parse('Hello www.example.com World!')
->detectUrls()
->getCode();
#output: Hello [url]www.example.com[/url] World!

View file

@ -1,63 +0,0 @@
<?php
include '../Parserus.php';
$parser = new Parserus();
echo $parser->setBBCodes([
['tag' => 'url',
'type' => 'url',
'parents' => ['inline', 'block'],
'attrs' => [
'Def' => [
'format' => '%^[^\x00-\x1f]+$%',
],
'no attr' => [
'body format' => '%^[^\x00-\x1f]+$%D',
],
],
'handler' => function($body, $attrs, $parser) {
#...
},
],
['tag' => 'h',
'type' => 'h',
'handler' => function($body, $attrs, $parser) {
#...
},
],
])->parse('www.example.com/link1[h]Hello www.example.com/link2 World![/h]www.example.com/link3')
->detectUrls()
->getCode();
#output: [url]www.example.com/link1[/url][h]Hello www.example.com/link2 World![/h][url]www.example.com/link3[/url]
echo "\n\n";
echo $parser->setBlackList(['url'])
->setWhiteList()
->parse('www.example.com/link1[h]Hello www.example.com/link2 World![/h]www.example.com/link3')
->detectUrls()
->getCode();
#output: www.example.com/link1[h]Hello www.example.com/link2 World![/h]www.example.com/link3
var_dump($parser->getErrors());
#output: array (size=1)
#output: 0 => string 'Тег [url] находится в черном списке' (length=60)
echo "\n\n";
echo $parser->setBlackList()
->setWhiteList(['h'])
->parse('www.example.com/link1[h]Hello www.example.com/link2 World![/h]www.example.com/link3')
->detectUrls()
->getCode();
#output: www.example.com/link1[h]Hello www.example.com/link2 World![/h]www.example.com/link3
var_dump($parser->getErrors());
#output: array (size=1)
#output: 0 => string 'Тег [url] отсутствует в белом списке' (length=62)

View file

@ -1,9 +0,0 @@
<?php
include '../Parserus.php';
$parser = new Parserus();
echo $parser->e("<'abcde'>");
#output: &lt;&apos;abcde&apos;&gt;

View file

@ -1,126 +0,0 @@
<?php
include '../Parserus.php';
$parser = new Parserus();
echo $parser->setBBCodes([
['tag' => 'table',
'type' => 'table',
'tags only' => true,
'self nesting' => true,
'attrs' => [
'no attr' => true,
'style' => true,
'align' => true,
'background' => true,
'bgcolor' => true,
'border' => true,
'bordercolor' => true,
'cellpadding' => true,
'cellspacing' => true,
'frame' => true,
'rules' => true,
],
'handler' => function($body, $attrs) {
$attr = '';
foreach ($attrs as $key => $val) {
$attr .= ' ' . $key . '="' . $val . '"';
}
return '<table' . $attr . '>' . $body . '</table>';
},
],
['tag' => 'tr',
'type' => 'tr',
'parents' => ['table', 't'],
'tags only' => true,
'self nesting' => true,
'attrs' => [
'no attr' => true,
'style' => true,
],
'handler' => function($body, $attrs) {
$attr = '';
foreach ($attrs as $key => $val) {
$attr .= ' ' . $key . '="' . $val . '"';
}
return '<tr' . $attr . '>' . $body . '</tr>';
},
],
['tag' => 'th',
'type' => 'block',
'parents' => ['tr'],
'self nesting' => true,
'attrs' => [
'no attr' => true,
'style' => true,
'colspan' => true,
'rowspan' => true,
],
'handler' => function($body, $attrs) {
$attr = '';
foreach ($attrs as $key => $val) {
$attr .= ' ' . $key . '="' . $val . '"';
}
return '<th' . $attr . '>' . $body . '</th>';
},
],
['tag' => 'td',
'type' => 'block',
'parents' => ['tr'],
'self nesting' => true,
'attrs' => [
'no attr' => true,
'style' => true,
'colspan' => true,
'rowspan' => true,
],
'handler' => function($body, $attrs) {
$attr = '';
foreach ($attrs as $key => $val) {
$attr .= ' ' . $key . '="' . $val . '"';
}
return '<td' . $attr . '>' . $body . '</td>';
},
],
])->parse('
[table align=right border=1 bordercolor=#ccc cellpadding=5 cellspacing=0 style="border-collapse:collapse; width:500px"]
[tr]
[th style="width:50%"]Position[/th]
[th style=width:50%]Astronaut[/th]
[/tr]
[tr]
[td]Commander[/td]
[td]Neil A. Armstrong[/td]
[/tr]
[tr]
[td]Command Module Pilot[/td]
[td]Michael Collins[/td]
[/tr]
[tr]
[td]Lunar Module Pilot[/td]
[td]Edwin "Buzz" E. Aldrin, Jr.[/td]
[/tr]
[/table]
')->getCode();
#output:
#[table align="right" border="1" bordercolor="#ccc" cellpadding="5" cellspacing="0" style="border-collapse:collapse; width:500px"]
# [tr]
# [th style=width:50%]Position[/th]
# [th style=width:50%]Astronaut[/th]
# [/tr]
# [tr]
# [td]Commander[/td]
# [td]Neil A. Armstrong[/td]
# [/tr]
# [tr]
# [td]Command Module Pilot[/td]
# [td]Michael Collins[/td]
# [/tr]
# [tr]
# [td]Lunar Module Pilot[/td]
# [td]Edwin "Buzz" E. Aldrin, Jr.[/td]
# [/tr]
#[/table]
#

View file

@ -1,41 +0,0 @@
<?php
include '../Parserus.php';
$parser = new Parserus();
$parser->addBBCode([
'tag' => 'b',
'handler' => function($body) {
return '<b>' . $body . '</b>';
}
])->addBBcode([
'tag' => 'i',
'handler' => function($body) {
return '<i>' . $body . '</i>';
},
]);
$parser->parse("[i][b] [/b][/i]")->stripEmptyTags(" \n", true);
$err = [
1 => '[%1$s] is in the black list',
2 => '[%1$s] is absent in the white list',
3 => '[%1$s] can\'t be opened in the [%2$s]',
4 => '[/%1$s] was found without a matching [%1$s]',
5 => '[/%1$s] is found for single [%1$s]',
6 => 'There are no attributes in [%1$s]',
7 => 'Primary attribute is forbidden in [%1$s=...]',
8 => 'Secondary attributes are forbidden in [%1$s ...]',
9 => 'The attribute \'%2$s\' doesn\'t correspond to a template in the [%1$s]',
10 => '[%1$s ...] contains unknown secondary attribute \'%2$s\'',
11 => 'The body of [%1$s] doesn\'t correspond to a template',
12 => '[%1$s] was opened within itself, this is not allowed',
13 => 'In the [%1$s] is absent mandatory attribute \'%2$s\'',
14 => 'All tags are empty'
];
var_dump($parser->getErrors($err));
#output: array (size=1)
# 0 => string 'All tags are empty' (length=18)

View file

@ -1,34 +0,0 @@
<?php
include '../Parserus.php';
$parser = new Parserus();
echo $parser->setSmilies([
':)' => 'http://example.com/smilies/smile.png',
';)' => 'http://example.com/smilies/wink.png',
])->addBBCode([
'tag' => 'img',
'type' => 'img',
'parents' => ['inline', 'block', 'url'],
'text only' => true,
'attrs' => [
'Def' => [
'body format' => '%^(?:(?:ht|f)tps?://[^\x00-\x1f\s<"]+|data:image/[a-z]+;base64,(?:[a-zA-Z\d/\+\=]+))$%D'
],
'no attr' => [
'body format' => '%^(?:(?:ht|f)tps?://[^\x00-\x1f\s<"]+|data:image/[a-z]+;base64,(?:[a-zA-Z\d/\+\=]+))$%D'
],
],
'handler' => function($body, $attrs, $parser) {
if (! isset($attrs['Def'])) {
$attrs['Def'] = (substr($body, 0, 11) === 'data:image/') ? 'base64' : basename($body);
}
return '<img src="' . $body . '" alt="' . $attrs['Def'] . '">';
},
])->setSmTpl('<img src="{url}" alt="{alt}">')
->parse(":)\n;)")
->detectSmilies()
->getHTML();
#output: <img src="http://example.com/smilies/smile.png" alt=":)"><br><img src="http://example.com/smilies/wink.png" alt=";)">

View file

@ -1,61 +0,0 @@
<?php
include '../Parserus.php';
$parser = new Parserus();
$parser->addBBCode([
'tag' => 'b',
'handler' => function($body) {
return '<b>' . $body . '</b>';
}
])->addBBcode([
'tag' => 'i',
'handler' => function($body) {
return '<i>' . $body . '</i>';
},
]);
# №1
var_dump($parser->parse("[i][b] [/b][/i]")->stripEmptyTags());
#output: boolean false
echo $parser->getCode();
#output: [i][b] [/b][/i]
echo "\n\n";
# №2
var_dump($parser->parse("[i][b] [/b][/i]")->stripEmptyTags(" \n", true));
#output: boolean true
echo $parser->getCode();
#output: [i][b] [/b][/i]
var_dump($parser->getErrors());
#output: array (size=1)
# 0 => string 'Все теги пустые' (length=28)
echo "\n\n";
# №3
var_dump($parser->parse("[i][b] [/b][/i]")->stripEmptyTags(" \n"));
#output: boolean true
echo $parser->getCode();
#output:
var_dump($parser->getErrors());
#output: array (size=1)
# empty