diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 3b5f15b08a..24af50bd7b 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -23,6 +23,7 @@ Yii Framework 2 Change Log
- Bug #12939: Hard coded table names for MSSQL in RBAC migration (arogachev)
- Bug #12974: Fixed incorrect order of migrations history in case `yii\console\controllers\MigrateController::$migrationNamespaces` is in use (evgen-d, klimov-paul)
- Bug #13071: Help option for commands was not working in modules (arogachev, haimanman)
+- Bug #7727: Fixed truncateHtml leaving extra tags (developeruz)
- Enh #6809: Added `\yii\caching\Cache::$defaultDuration` property, allowing to set custom default cache duration (sdkiller)
- Enh #7333: Improved error message for `yii\di\Instance::ensure()` when a component does not exist (cebe)
- Enh #7420: Attributes for prompt generated with `renderSelectOptions` of `\yii\helpers\Html` helper (arogachev)
diff --git a/framework/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php
index 4fd0d2edd5..dc98f4de13 100644
--- a/framework/helpers/BaseStringHelper.php
+++ b/framework/helpers/BaseStringHelper.php
@@ -155,13 +155,15 @@ class BaseStringHelper
$config->set('Cache.SerializerPath', \Yii::$app->getRuntimePath());
$lexer = \HTMLPurifier_Lexer::create($config);
$tokens = $lexer->tokenizeHTML($string, $config, null);
- $openTokens = 0;
+ $openTokens = [];
$totalCount = 0;
$truncated = [];
foreach ($tokens as $token) {
if ($token instanceof \HTMLPurifier_Token_Start) { //Tag begins
- $openTokens++;
- $truncated[] = $token;
+ if ($totalCount < $count) {
+ $openTokens[$token->name] = isset($openTokens[$token->name]) ? $openTokens[$token->name] + 1 : 1;
+ $truncated[] = $token;
+ }
} elseif ($token instanceof \HTMLPurifier_Token_Text && $totalCount <= $count) { //Text
if (false === $encoding) {
preg_match('/^(\s*)/um', $token->data, $prefixSpace) ?: $prefixSpace = ['',''];
@@ -174,8 +176,10 @@ class BaseStringHelper
$totalCount += $currentCount;
$truncated[] = $token;
} elseif ($token instanceof \HTMLPurifier_Token_End) { //Tag ends
- $openTokens--;
- $truncated[] = $token;
+ if (!empty($openTokens[$token->name])) {
+ $openTokens[$token->name]--;
+ $truncated[] = $token;
+ }
} elseif ($token instanceof \HTMLPurifier_Token_Empty) { //Self contained tags, i.e. etc.
$truncated[] = $token;
}
diff --git a/tests/framework/helpers/StringHelperTest.php b/tests/framework/helpers/StringHelperTest.php
index cb210a538d..12689d51a2 100644
--- a/tests/framework/helpers/StringHelperTest.php
+++ b/tests/framework/helpers/StringHelperTest.php
@@ -115,6 +115,8 @@ class StringHelperTest extends TestCase
$this->assertEquals('
This is a test...', StringHelper::truncate('
This is a test sentance', 14, '...', null, true));
$this->assertEquals('
This is a test...', StringHelper::truncate('
This is a test sentance', 14, '...', null, true));
$this->assertEquals('
This is a test for...', StringHelper::truncate('
This is a test for a sentance', 18, '...', null, true));
+
+ $this->assertEquals('
This is a test
This is a test