Extracted methods to a class
This commit is contained in:
parent
dbfce2ae54
commit
0e2f17ac37
189
src/MicrodataDOMElement.php
Normal file
189
src/MicrodataDOMElement.php
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace YusufKandemir\MicrodataParser;
|
||||||
|
|
||||||
|
class MicrodataDOMElement extends \DOMElement
|
||||||
|
{
|
||||||
|
public function getProperties()
|
||||||
|
{
|
||||||
|
// Step 1
|
||||||
|
$results = [];
|
||||||
|
$memory = [];
|
||||||
|
$pending = [];
|
||||||
|
|
||||||
|
// Step 2
|
||||||
|
$memory[] = $this;
|
||||||
|
|
||||||
|
// Step 3
|
||||||
|
if ($this->hasChildNodes()) {
|
||||||
|
$childNodes = iterator_to_array($this->childNodes);
|
||||||
|
|
||||||
|
$childNodes = array_filter($childNodes, function ($node) {
|
||||||
|
return $node instanceof \DOMElement;
|
||||||
|
}); // Get only DOMElements
|
||||||
|
|
||||||
|
$pending = array_merge($pending, $childNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4
|
||||||
|
if ($this->hasAttribute('itemref')) {
|
||||||
|
$tokens = preg_split('/\s+/', $this->getAttribute('itemref'));
|
||||||
|
|
||||||
|
foreach ($tokens as $token) {
|
||||||
|
// @todo Implement xpath query and get the first item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5
|
||||||
|
while ($pending) {
|
||||||
|
// Step 6
|
||||||
|
$current = array_pop($pending);
|
||||||
|
|
||||||
|
// Step 7
|
||||||
|
// in_array can't compare objects
|
||||||
|
/*if (in_array($current, $memory)) {
|
||||||
|
// There is MicrodataError
|
||||||
|
continue;
|
||||||
|
}*/
|
||||||
|
$error = false;
|
||||||
|
|
||||||
|
foreach ($memory as $memory_item) {
|
||||||
|
if ($current->isSameNode($memory_item)) {
|
||||||
|
// There is MicrodataError
|
||||||
|
$error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($error) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 8
|
||||||
|
$memory[] = $current;
|
||||||
|
|
||||||
|
// Step 9
|
||||||
|
if (! $current->hasAttribute('itemscope')) {
|
||||||
|
if ($current->hasChildNodes()) {
|
||||||
|
$childNodes = iterator_to_array($current->childNodes);
|
||||||
|
|
||||||
|
$childNodes = array_filter($childNodes, function ($node) {
|
||||||
|
return $node instanceof \DOMElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
$pending = array_merge($pending, $childNodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 10
|
||||||
|
if ($current->hasAttribute('itemprop') && /* hasPropertyNames */ $current->getPropertyNames()) {
|
||||||
|
$results[] = $current;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 11: Return to loop
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 12: End of loop. Sort results in tree order.
|
||||||
|
|
||||||
|
$results = array_reverse($results);
|
||||||
|
|
||||||
|
// Step 13
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPropertyNames()
|
||||||
|
{
|
||||||
|
// Step 1
|
||||||
|
$itemprop = $this->getAttribute('itemprop');
|
||||||
|
$tokens = $itemprop ? preg_split('/\s+/', $itemprop) : [];
|
||||||
|
|
||||||
|
// Step 2
|
||||||
|
$properties = [];
|
||||||
|
|
||||||
|
// Step 3
|
||||||
|
foreach ($tokens as $token) {
|
||||||
|
if ($this->isAbsoluteUri($token)) {
|
||||||
|
$properties[] = $token;
|
||||||
|
} elseif ($this->isTypedItem()) {
|
||||||
|
$properties[] = /*$vocabularyIdentifier . */ $token;
|
||||||
|
} else {
|
||||||
|
$properties[] = $token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$properties = array_unique($properties);
|
||||||
|
|
||||||
|
return $properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPropertyValue()
|
||||||
|
{
|
||||||
|
|
||||||
|
if ($this->hasAttribute('itemscope')) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->hasAttribute('content')) {
|
||||||
|
return $this->getAttribute('content');
|
||||||
|
}
|
||||||
|
|
||||||
|
$base = $this->ownerDocument->documentURI;
|
||||||
|
|
||||||
|
switch ($this->tagName) {
|
||||||
|
case 'audio':
|
||||||
|
case 'embed':
|
||||||
|
case 'iframe':
|
||||||
|
case 'img':
|
||||||
|
case 'source':
|
||||||
|
case 'track':
|
||||||
|
case 'video':
|
||||||
|
if ($this->hasAttribute('src')) {
|
||||||
|
$result = $this->getAttribute('src');
|
||||||
|
|
||||||
|
// @todo check against protocol relative urls like "//example.com/test.jpg"
|
||||||
|
return $this->isAbsoluteUri($result) ? $result : $base.$result;
|
||||||
|
}
|
||||||
|
case 'a':
|
||||||
|
case 'area':
|
||||||
|
case 'link':
|
||||||
|
if ($this->hasAttribute('href')) {
|
||||||
|
$result = $this->getAttribute('href');
|
||||||
|
|
||||||
|
return $this->isAbsoluteUri($result) ? $result : $base.$result;
|
||||||
|
}
|
||||||
|
case 'object':
|
||||||
|
if ($this->hasAttribute('data')) {
|
||||||
|
$result = $this->getAttribute('data');
|
||||||
|
|
||||||
|
return $this->isAbsoluteUri($result) ? $result : $base.$result;
|
||||||
|
}
|
||||||
|
case 'data':
|
||||||
|
case 'meter':
|
||||||
|
if ($this->hasAttribute('value')) {
|
||||||
|
return $this->getAttribute('value');
|
||||||
|
}
|
||||||
|
case 'time':
|
||||||
|
if ($this->hasAttribute('datetime')) {
|
||||||
|
return $this->getAttribute('datetime');
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return $this->textContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isTypedItem()
|
||||||
|
{
|
||||||
|
$tokens = [];
|
||||||
|
|
||||||
|
if ($this->hasAttribute('itemtype')) {
|
||||||
|
$tokens = preg_split("/\s+/", $this->getAttribute('itemtype'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return !empty($tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function isAbsoluteUri(string $uri)
|
||||||
|
{
|
||||||
|
return preg_match("/^\w+:/", trim($uri));
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,8 @@ class MicrodataParser
|
|||||||
|
|
||||||
public function __construct(\DOMDocument $dom)
|
public function __construct(\DOMDocument $dom)
|
||||||
{
|
{
|
||||||
|
$dom->registerNodeClass(\DOMElement::class, MicrodataDOMElement::class);
|
||||||
|
|
||||||
$xpath = new \DOMXPath($dom);
|
$xpath = new \DOMXPath($dom);
|
||||||
$this->topLevelItems = $xpath->query('//*[@itemscope and not(@itemprop)]');
|
$this->topLevelItems = $xpath->query('//*[@itemscope and not(@itemprop)]');
|
||||||
}
|
}
|
||||||
@ -61,8 +63,8 @@ class MicrodataParser
|
|||||||
$properties = new \stdClass;
|
$properties = new \stdClass;
|
||||||
|
|
||||||
// Step 7
|
// Step 7
|
||||||
foreach ($this->getProperties($item) as $element) {
|
foreach ($item->getProperties() as $element) {
|
||||||
$value = $this->getPropertyValue($element);
|
$value = $element->getPropertyValue();
|
||||||
|
|
||||||
if ($this->isItem($value)) {
|
if ($this->isItem($value)) {
|
||||||
foreach ($memory as $memory_item) {
|
foreach ($memory as $memory_item) {
|
||||||
@ -76,7 +78,7 @@ class MicrodataParser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->getPropertyNames($element) as $name) {
|
foreach ($element->getPropertyNames() as $name) {
|
||||||
$properties->{$name}[] = $value;
|
$properties->{$name}[] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,191 +90,8 @@ class MicrodataParser
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getPropertyNames(\DOMElement $item)
|
|
||||||
{
|
|
||||||
// Step 1
|
|
||||||
$itemprop = $item->getAttribute('itemprop');
|
|
||||||
$tokens = $itemprop ? preg_split('/\s+/', $itemprop) : [];
|
|
||||||
|
|
||||||
// Step 2
|
|
||||||
$properties = [];
|
|
||||||
|
|
||||||
// Step 3
|
|
||||||
foreach ($tokens as $token) {
|
|
||||||
if ($this->isAbsoluteUri($token)) {
|
|
||||||
$properties[] = $token;
|
|
||||||
} elseif ($this->isTypedItem($item)) {
|
|
||||||
$properties[] = /*$vocabularyIdentifier . */ $token;
|
|
||||||
} else {
|
|
||||||
$properties[] = $token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$properties = array_unique($properties);
|
|
||||||
|
|
||||||
return $properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getProperties(\DOMElement $root)
|
|
||||||
{
|
|
||||||
// Step 1
|
|
||||||
$results = [];
|
|
||||||
$memory = [];
|
|
||||||
$pending = [];
|
|
||||||
|
|
||||||
// Step 2
|
|
||||||
$memory[] = $root;
|
|
||||||
|
|
||||||
// Step 3
|
|
||||||
if ($root->hasChildNodes()) {
|
|
||||||
$childNodes = iterator_to_array($root->childNodes);
|
|
||||||
|
|
||||||
$childNodes = array_filter($childNodes, function ($node) {
|
|
||||||
return $node instanceof \DOMElement;
|
|
||||||
}); // Get only DOMElements
|
|
||||||
|
|
||||||
$pending = array_merge($pending, $childNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4
|
|
||||||
if ($root->hasAttribute('itemref')) {
|
|
||||||
$tokens = preg_split('/\s+/', $root->getAttribute('itemref'));
|
|
||||||
|
|
||||||
foreach ($tokens as $token) {
|
|
||||||
// @todo Implement xpath query and get the first item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 5
|
|
||||||
while ($pending) {
|
|
||||||
// Step 6
|
|
||||||
$current = array_pop($pending);
|
|
||||||
|
|
||||||
// Step 7
|
|
||||||
// in_array can't compare objects
|
|
||||||
/*if (in_array($current, $memory)) {
|
|
||||||
// There is MicrodataError
|
|
||||||
continue;
|
|
||||||
}*/
|
|
||||||
$error = false;
|
|
||||||
|
|
||||||
foreach ($memory as $memory_item) {
|
|
||||||
if ($current->isSameNode($memory_item)) {
|
|
||||||
// There is MicrodataError
|
|
||||||
$error = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($error) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 8
|
|
||||||
$memory[] = $current;
|
|
||||||
|
|
||||||
// Step 9
|
|
||||||
if (! $current->hasAttribute('itemscope')) {
|
|
||||||
if ($current->hasChildNodes()) {
|
|
||||||
$childNodes = iterator_to_array($current->childNodes);
|
|
||||||
|
|
||||||
$childNodes = array_filter($childNodes, function ($node) {
|
|
||||||
return $node instanceof \DOMElement;
|
|
||||||
});
|
|
||||||
|
|
||||||
$pending = array_merge($pending, $childNodes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 10
|
|
||||||
if ($current->hasAttribute('itemprop') && /* hasPropertyNames */ $this->getPropertyNames($current)) {
|
|
||||||
$results[] = $current;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 11: Return to loop
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 12: End of loop. Sort results in tree order.
|
|
||||||
|
|
||||||
$results = array_reverse($results);
|
|
||||||
|
|
||||||
// Step 13
|
|
||||||
return $results;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getPropertyValue(\DOMElement $item)
|
|
||||||
{
|
|
||||||
|
|
||||||
if ($item->hasAttribute('itemscope')) {
|
|
||||||
return $item;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($item->hasAttribute('content')) {
|
|
||||||
return $item->getAttribute('content');
|
|
||||||
}
|
|
||||||
|
|
||||||
$base = $item->ownerDocument->documentURI;
|
|
||||||
|
|
||||||
switch ($item->tagName) {
|
|
||||||
case 'audio':
|
|
||||||
case 'embed':
|
|
||||||
case 'iframe':
|
|
||||||
case 'img':
|
|
||||||
case 'source':
|
|
||||||
case 'track':
|
|
||||||
case 'video':
|
|
||||||
if ($item->hasAttribute('src')) {
|
|
||||||
$result = $item->getAttribute('src');
|
|
||||||
|
|
||||||
// @todo check against protocol relative urls like "//example.com/test.jpg"
|
|
||||||
return $this->isAbsoluteUri($result) ? $result : $base.$result;
|
|
||||||
}
|
|
||||||
case 'a':
|
|
||||||
case 'area':
|
|
||||||
case 'link':
|
|
||||||
if ($item->hasAttribute('href')) {
|
|
||||||
$result = $item->getAttribute('href');
|
|
||||||
|
|
||||||
return $this->isAbsoluteUri($result) ? $result : $base.$result;
|
|
||||||
}
|
|
||||||
case 'object':
|
|
||||||
if ($item->hasAttribute('data')) {
|
|
||||||
$result = $item->getAttribute('data');
|
|
||||||
|
|
||||||
return $this->isAbsoluteUri($result) ? $result : $base.$result;
|
|
||||||
}
|
|
||||||
case 'data':
|
|
||||||
case 'meter':
|
|
||||||
if ($item->hasAttribute('value')) {
|
|
||||||
return $item->getAttribute('value');
|
|
||||||
}
|
|
||||||
case 'time':
|
|
||||||
if ($item->hasAttribute('datetime')) {
|
|
||||||
return $item->getAttribute('datetime');
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return $item->textContent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function isItem($element)
|
protected function isItem($element)
|
||||||
{
|
{
|
||||||
return $element instanceof \DOMElement && $element->hasAttribute('itemscope');
|
return $element instanceof \DOMElement && $element->hasAttribute('itemscope');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isTypedItem(\DOMElement $item)
|
|
||||||
{
|
|
||||||
$tokens = [];
|
|
||||||
|
|
||||||
if ($item->hasAttribute('itemtype')) {
|
|
||||||
$tokens = preg_split("/\s+/", $item->getAttribute('itemtype'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return !empty($tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function isAbsoluteUri(string $uri)
|
|
||||||
{
|
|
||||||
return preg_match("/^\w+:/", trim($uri));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user