Extracted methods to a class
This commit is contained in:
		
							
								
								
									
										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)
 | 
			
		||||
    {
 | 
			
		||||
        $dom->registerNodeClass(\DOMElement::class, MicrodataDOMElement::class);
 | 
			
		||||
 | 
			
		||||
        $xpath = new \DOMXPath($dom);
 | 
			
		||||
        $this->topLevelItems = $xpath->query('//*[@itemscope and not(@itemprop)]');
 | 
			
		||||
    }
 | 
			
		||||
@@ -61,8 +63,8 @@ class MicrodataParser
 | 
			
		||||
        $properties = new \stdClass;
 | 
			
		||||
 | 
			
		||||
        // Step 7
 | 
			
		||||
        foreach ($this->getProperties($item) as $element) {
 | 
			
		||||
            $value = $this->getPropertyValue($element);
 | 
			
		||||
        foreach ($item->getProperties() as $element) {
 | 
			
		||||
            $value = $element->getPropertyValue();
 | 
			
		||||
 | 
			
		||||
            if ($this->isItem($value)) {
 | 
			
		||||
                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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -88,191 +90,8 @@ class MicrodataParser
 | 
			
		||||
        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)
 | 
			
		||||
    {
 | 
			
		||||
        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));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user