Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
"keywords": ["twig extension", "truncate", "twig"],
"type": "library",
"require": {
"php": ">=5.3.0",
"antoligy/dom-string-iterators": "v1.0.0",
"twig/twig": ">=v1.0.0"
"php": "^8.1",
"antoligy/dom-string-iterators": "^v1.0.0",
"twig/twig": ">=v1.0.0",
"ext-dom": "*",
"ext-libxml": "*"
},
"license": "MIT",
"authors": [
Expand Down
78 changes: 39 additions & 39 deletions src/TruncateExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,47 @@

namespace Bluetel\Twig;

use DOMElement;
use DOMNode;
use DOMText;
use DOMDocument;
use DOMWordsIterator;
use DOMLettersIterator;
use Twig_Extension;
use Twig_SimpleFilter;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;

/**
* TruncateExtension
* @author Alex Wilson <[email protected]>
* @license MIT
*/
class TruncateExtension extends Twig_Extension
class TruncateExtension extends AbstractExtension
{
/**
* @return array Returns the list of filters supplied by this extension.
* @return array<string, TwigFilter> Returns the list of filters supplied by this extension.
*/
public function getFilters()
public function getFilters(): array
{
$truncateWords = new Twig_SimpleFilter(
$truncateWords = new TwigFilter(
'truncate_words',
array($this, 'truncateWords'),
array(
'is_safe' => array('html'),
)
[$this, 'truncateWords'],
[
'is_safe' => ['html'],
]
);

$truncateLetters = new Twig_SimpleFilter(
$truncateLetters = new TwigFilter(
'truncate_letters',
array($this, 'truncateLetters'),
array(
'is_safe' => array('html'),
)
[$this, 'truncateLetters'],
[
'is_safe' => ['html'],
]
);

return array(
return [
'truncate_letters' => $truncateWords,
'truncate_words' => $truncateLetters,
);
];
}

/**
Expand All @@ -50,7 +52,7 @@ public function getFilters()
* @param string $ellipsis String to use as ellipsis (if any).
* @return string Safe truncated HTML.
*/
public function truncateWords($html, $limit = 0, $ellipsis = "")
public function truncateWords(string $html, int $limit = 0, string $ellipsis = ''): string
{
if ($limit <= 0) {
return $html;
Expand All @@ -59,7 +61,7 @@ public function truncateWords($html, $limit = 0, $ellipsis = "")
$dom = $this->htmlToDomDocument($html);

// Grab the body of our DOM.
$body = $dom->getElementsByTagName("body")->item(0);
$body = $dom->getElementsByTagName('body')->item(0);

// Iterate over words.
$words = new DOMWordsIterator($body);
Expand All @@ -77,7 +79,7 @@ public function truncateWords($html, $limit = 0, $ellipsis = "")
$curNode->nodeValue = substr(
$curNode->nodeValue,
0,
$words[$offset][1] + strlen($words[$offset][0])
$words[$offset][1] + \strlen($words[$offset][0])
);

self::removeProceedingNodes($curNode, $body);
Expand All @@ -91,7 +93,7 @@ public function truncateWords($html, $limit = 0, $ellipsis = "")

}

return $dom->saveHTML();
return (\is_string($dom->saveHTML())) ? $dom->saveHTML() : '';
}

/**
Expand All @@ -101,7 +103,7 @@ public function truncateWords($html, $limit = 0, $ellipsis = "")
* @param string $ellipsis String to use as ellipsis (if any).
* @return string Safe truncated HTML.
*/
public function truncateLetters($html, $limit = 0, $ellipsis = "")
public function truncateLetters(string $html, int $limit = 0, string $ellipsis = ''): string
{
if ($limit <= 0) {
return $html;
Expand All @@ -110,7 +112,7 @@ public function truncateLetters($html, $limit = 0, $ellipsis = "")
$dom = $this->htmlToDomDocument($html);

// Grab the body of our DOM.
$body = $dom->getElementsByTagName("body")->item(0);
$body = $dom->getElementsByTagName('body')->item(0);

// Iterate over letters.
$letters = new DOMLettersIterator($body);
Expand All @@ -131,15 +133,15 @@ public function truncateLetters($html, $limit = 0, $ellipsis = "")
}
}

return $dom->saveHTML();
return (\is_string($dom->saveHTML())) ? $dom->saveHTML() : '';
}

/**
* Builds a DOMDocument object from a string containing HTML.
* @param string HTML to load
* @param string $html HTML to load
* @returns DOMDocument Returns a DOMDocument object.
*/
public function htmlToDomDocument($html)
public function htmlToDomDocument(string $html): DOMDocument
{
// Transform multibyte entities which otherwise display incorrectly.
$html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
Expand All @@ -157,25 +159,23 @@ public function htmlToDomDocument($html)

/**
* Removes all nodes after the current node.
* @param DOMNode|DOMElement $domNode
* @param DOMNode|DOMElement $topNode
* @return void
*/
private static function removeProceedingNodes($domNode, $topNode)
private static function removeProceedingNodes(DOMNode $domNode, ?DOMNode $topNode): void
{
$nextNode = $domNode->nextSibling;

if ($nextNode !== null) {
self::removeProceedingNodes($nextNode, $topNode);
$domNode->parentNode->removeChild($nextNode);
$domNode->parentNode?->removeChild($nextNode);
} else {
//scan upwards till we find a sibling
$curNode = $domNode->parentNode;
while ($curNode !== $topNode) {
while ($curNode !== $topNode && $topNode !== null) {
if ($curNode->nextSibling !== null) {
$curNode = $curNode->nextSibling;
self::removeProceedingNodes($curNode, $topNode);
$curNode->parentNode->removeChild($curNode);
$curNode?->parentNode->removeChild($curNode);
break;
}
$curNode = $curNode->parentNode;
Expand All @@ -185,15 +185,15 @@ private static function removeProceedingNodes($domNode, $topNode)

/**
* Inserts an ellipsis
* @param DOMNode|DOMElement $domNode Element to insert after.
* @param string $ellipsis Text used to suffix our document.
* @param DOMNode $domNode Element to insert after.
* @param string $ellipsis Text used to suffix our document.
* @return void
*/
private static function insertEllipsis($domNode, $ellipsis)
private static function insertEllipsis(DOMNode $domNode, string $ellipsis): void
{
$avoid = array('a', 'strong', 'em', 'h1', 'h2', 'h3', 'h4', 'h5'); //html tags to avoid appending the ellipsis to
$avoid = ['a', 'strong', 'em', 'h1', 'h2', 'h3', 'h4', 'h5']; //html tags to avoid appending the ellipsis to

if (in_array($domNode->parentNode->nodeName, $avoid) && $domNode->parentNode->parentNode !== null) {
if ($domNode->parentNode->parentNode !== null && in_array($domNode->parentNode->nodeName, $avoid)) {
// Append as text node to parent instead
$textNode = new DOMText($ellipsis);

Expand All @@ -205,15 +205,15 @@ private static function insertEllipsis($domNode, $ellipsis)

} else {
// Append to current node
$domNode->nodeValue = rtrim($domNode->nodeValue) . $ellipsis;
$domNode->nodeValue = rtrim($domNode->nodeValue ?? '') . $ellipsis;
}
}

/**
* Returns the name of this extension.
* @return string
*/
public function getName()
public function getName(): string
{
return 'truncate_extension';
}
Expand Down