diff --git a/Mf2/Parser.php b/Mf2/Parser.php index 5c26f80..92f732e 100644 --- a/Mf2/Parser.php +++ b/Mf2/Parser.php @@ -13,17 +13,17 @@ /** * Parse Microformats2 - * + * * Functional shortcut for the commonest cases of parsing microformats2 from HTML. - * + * * Example usage: - * + * * use Mf2; * $output = Mf2\parse('Barnaby Walters'); * echo json_encode($output, JSON_PRETTY_PRINT); - * + * * Produces: - * + * * { * "items": [ * { @@ -35,7 +35,7 @@ * ], * "rels": {} * } - * + * * @param string|DOMDocument $input The HTML string or DOMDocument object to parse * @param string $url The URL the input document was found at, for relative URL resolution * @param bool $convertClassic whether or not to convert classic microformats @@ -84,7 +84,7 @@ function fetch($url, $convertClassic = true, &$curlInfo=null) { /** * Unicode to HTML Entities * @param string $input String containing characters to convert into HTML entities - * @return string + * @return string */ function unicodeToHtmlEntities($input) { return mb_convert_encoding($input, 'HTML-ENTITIES', mb_detect_encoding($input)); @@ -92,10 +92,10 @@ function unicodeToHtmlEntities($input) { /** * Collapse Whitespace - * + * * Collapses any sequences of whitespace within a string into a single space * character. - * + * * @deprecated since v0.2.3 * @param string $str * @return string @@ -113,10 +113,10 @@ function unicodeTrim($str) { /** * Microformat Name From Class string - * - * Given the value of @class, get the relevant mf classnames (e.g. h-card, + * + * Given the value of @class, get the relevant mf classnames (e.g. h-card, * p-name). - * + * * @param string $class A space delimited list of classnames * @param string $prefix The prefix to look for * @return string|array The prefixed name of the first microfomats class found or false @@ -139,10 +139,10 @@ function mfNamesFromClass($class, $prefix='h-') { /** * Get Nested µf Property Name From Class - * - * Returns all the p-, u-, dt- or e- prefixed classnames it finds in a + * + * Returns all the p-, u-, dt- or e- prefixed classnames it finds in a * space-separated string. - * + * * @param string $class * @return array */ @@ -165,7 +165,7 @@ function nestedMfPropertyNamesFromClass($class) { /** * Wraps mfNamesFromClass to handle an element as input (common) - * + * * @param DOMElement $e The element to get the classname for * @param string $prefix The prefix to look for * @return mixed See return value of mf2\Parser::mfNameFromClass() @@ -228,11 +228,11 @@ function convertTimeFormat($time) { /** * Microformats2 Parser - * + * * A class which holds state for parsing microformats2 from HTML. - * + * * Example usage: - * + * * use Mf2; * $parser = new Mf2\Parser('

Barnaby Walters

'); * $output = $parser->parse(); @@ -243,18 +243,18 @@ class Parser { /** @var DOMXPath object which can be used to query over any fragment*/ public $xpath; - + /** @var DOMDocument */ public $doc; - + /** @var SplObjectStorage */ protected $parsed; - + public $jsonMode; /** * Constructor - * + * * @param DOMDocument|string $input The data to parse. A string of HTML or a DOMDocument * @param string $url The URL of the parsed document, for relative URL resolution * @param boolean $jsonMode Whether or not to use a stdClass instance for an empty `rels` dictionary. This breaks PHP looping over rels, but allows the output to be correctly serialized as JSON. @@ -270,20 +270,20 @@ public function __construct($input, $url = null, $jsonMode = false) { $doc = new DOMDocument(); @$doc->loadHTML(''); } - + $this->xpath = new DOMXPath($doc); - + $baseurl = $url; foreach ($this->xpath->query('//base[@href]') as $base) { $baseElementUrl = $base->getAttribute('href'); - + if (parse_url($baseElementUrl, PHP_URL_SCHEME) === null) { /* The base element URL is relative to the document URL. * * :/ * * Perhaps the author was high? */ - + $baseurl = resolveUrl($url, $baseElementUrl); } else { $baseurl = $baseElementUrl; @@ -295,31 +295,31 @@ public function __construct($input, $url = null, $jsonMode = false) { foreach ($this->xpath->query('//template') as $templateEl) { $templateEl->parentNode->removeChild($templateEl); } - + $this->baseurl = $baseurl; $this->doc = $doc; $this->parsed = new SplObjectStorage(); $this->jsonMode = $jsonMode; } - + private function elementPrefixParsed(\DOMElement $e, $prefix) { if (!$this->parsed->contains($e)) $this->parsed->attach($e, array()); - + $prefixes = $this->parsed[$e]; $prefixes[] = $prefix; $this->parsed[$e] = $prefixes; } - + private function isElementParsed(\DOMElement $e, $prefix) { if (!$this->parsed->contains($e)) return false; - + $prefixes = $this->parsed[$e]; - + if (!in_array($prefix, $prefixes)) return false; - + return true; } @@ -351,72 +351,72 @@ public function textContent(DOMElement $el) { // TODO: figure out if this has problems with sms: and geo: URLs public function resolveUrl($url) { - // If the URL is seriously malformed it’s probably beyond the scope of this + // If the URL is seriously malformed it’s probably beyond the scope of this // parser to try to do anything with it. if (parse_url($url) === false) return $url; - + $scheme = parse_url($url, PHP_URL_SCHEME); - + if (empty($scheme) and !empty($this->baseurl)) { return resolveUrl($this->baseurl, $url); } else { return $url; } } - + // Parsing Functions - + /** - * Parse value-class/value-title on an element, joining with $separator if + * Parse value-class/value-title on an element, joining with $separator if * there are multiple. - * + * * @param \DOMElement $e * @param string $separator = '' if multiple value-title elements, join with this string * @return string|null the parsed value or null if value-class or -title aren’t in use */ public function parseValueClassTitle(\DOMElement $e, $separator = '') { $valueClassElements = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value ")]', $e); - + if ($valueClassElements->length !== 0) { // Process value-class stuff $val = ''; foreach ($valueClassElements as $el) { $val .= $this->textContent($el); } - + return unicodeTrim($val); } - + $valueTitleElements = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value-title ")]', $e); - + if ($valueTitleElements->length !== 0) { // Process value-title stuff $val = ''; foreach ($valueTitleElements as $el) { $val .= $el->getAttribute('title'); } - + return unicodeTrim($val); } - + // No value-title or -class in this element return null; } - + /** * Given an element with class="p-*", get it’s value - * + * * @param DOMElement $p The element to parse * @return string The plaintext value of $p, dependant on type * @todo Make this adhere to value-class */ public function parseP(\DOMElement $p) { $classTitle = $this->parseValueClassTitle($p, ' '); - + if ($classTitle !== null) return $classTitle; - + if ($p->tagName == 'img' and $p->getAttribute('alt') !== '') { $pValue = $p->getAttribute('alt'); } elseif ($p->tagName == 'area' and $p->getAttribute('alt') !== '') { @@ -428,13 +428,13 @@ public function parseP(\DOMElement $p) { } else { $pValue = unicodeTrim($this->textContent($p)); } - + return $pValue; } /** * Given an element with class="u-*", get the value of the URL - * + * * @param DOMElement $u The element to parse * @return string The plaintext value of $u, dependant on type * @todo make this adhere to value-class @@ -447,13 +447,13 @@ public function parseU(\DOMElement $u) { } elseif ($u->tagName == 'object' and $u->getAttribute('data') !== null) { $uValue = $u->getAttribute('data'); } - + if (isset($uValue)) { return $this->resolveUrl($uValue); } - + $classTitle = $this->parseValueClassTitle($u); - + if ($classTitle !== null) { return $classTitle; } elseif ($u->tagName == 'abbr' and $u->getAttribute('title') !== null) { @@ -467,7 +467,7 @@ public function parseU(\DOMElement $u) { /** * Given an element with class="dt-*", get the value of the datetime as a php date object - * + * * @param DOMElement $dt The element to parse * @param array $dates Array of dates processed so far * @return string The datetime string found @@ -476,11 +476,11 @@ public function parseDT(\DOMElement $dt, &$dates = array()) { // Check for value-class pattern $valueClassChildren = $this->xpath->query('./*[contains(concat(" ", @class, " "), " value ") or contains(concat(" ", @class, " "), " value-title ")]', $dt); $dtValue = false; - + if ($valueClassChildren->length > 0) { // They’re using value-class $dateParts = array(); - + foreach ($valueClassChildren as $e) { if (strstr(' ' . $e->getAttribute('class') . ' ', ' value-title ')) { $title = $e->getAttribute('title'); @@ -596,7 +596,7 @@ public function parseDT(\DOMElement $dt, &$dates = array()) { } /** - * if $dtValue is only a time and there are recently parsed dates, + * if $dtValue is only a time and there are recently parsed dates, * form the full date-time using the most recently parsed dt- value */ if ((preg_match('/^\d{1,2}:\d{1,2}(Z?[+|-]\d{2}:?\d{2})?/', $dtValue) or preg_match('/^\d{1,2}[a|p]m/', $dtValue)) && !empty($dates)) { @@ -612,15 +612,15 @@ public function parseDT(\DOMElement $dt, &$dates = array()) { * * @param DOMElement $e The element to parse * @return string $e’s innerHTML - * + * * @todo need to mark this element as e- parsed so it doesn’t get parsed as it’s parent’s e-* too */ public function parseE(\DOMElement $e) { $classTitle = $this->parseValueClassTitle($e); - + if ($classTitle !== null) return $classTitle; - + // Expand relative URLs within children of this element // TODO: as it is this is not relative to only children, make this .// and rerun tests $this->resolveChildUrls($e); @@ -629,7 +629,7 @@ public function parseE(\DOMElement $e) { foreach ($e->childNodes as $node) { $html .= $node->C14N(); } - + return array( 'html' => $html, 'value' => unicodeTrim($this->textContent($e)) @@ -638,7 +638,7 @@ public function parseE(\DOMElement $e) { /** * Recursively parse microformats - * + * * @param DOMElement $e The element to parse * @return array A representation of the values contained within microformat $e */ @@ -659,16 +659,16 @@ public function parseH(\DOMElement $e) { foreach ($this->xpath->query('.//*[contains(concat(" ", @class)," h-")]', $e) as $subMF) { // Parse $result = $this->parseH($subMF); - + // If result was already parsed, skip it if (null === $result) continue; - + $result['value'] = $this->parseP($subMF); // Does this µf have any property names other than h-*? $properties = nestedMfPropertyNamesFromElement($subMF); - + if (!empty($properties)) { // Yes! It’s a nested property µf foreach ($properties as $property) { @@ -678,7 +678,7 @@ public function parseH(\DOMElement $e) { // No, it’s a child µf $children[] = $result; } - + // Make sure this sub-mf won’t get parsed as a µf or property // TODO: Determine if clearing this is required? $this->elementPrefixParsed($subMF, 'h'); @@ -699,13 +699,13 @@ public function parseH(\DOMElement $e) { continue; $pValue = $this->parseP($p); - + // Add the value to the array for it’s p- properties foreach (mfNamesFromElement($p, 'p-') as $propName) { if (!empty($propName)) $return[$propName][] = $pValue; } - + // Make sure this sub-mf won’t get parsed as a top level mf $this->elementPrefixParsed($p, 'p'); } @@ -714,32 +714,32 @@ public function parseH(\DOMElement $e) { foreach ($this->xpath->query('.//*[contains(concat(" ", @class)," u-")]', $e) as $u) { if ($this->isElementParsed($u, 'u')) continue; - + $uValue = $this->parseU($u); - + // Add the value to the array for it’s property types foreach (mfNamesFromElement($u, 'u-') as $propName) { $return[$propName][] = $uValue; } - + // Make sure this sub-mf won’t get parsed as a top level mf $this->elementPrefixParsed($u, 'u'); } - + // Handle dt-* foreach ($this->xpath->query('.//*[contains(concat(" ", @class), " dt-")]', $e) as $dt) { if ($this->isElementParsed($dt, 'dt')) continue; - + $dtValue = $this->parseDT($dt, $dates); - + if ($dtValue) { // Add the value to the array for dt- properties foreach (mfNamesFromElement($dt, 'dt-') as $propName) { $return[$propName][] = $dtValue; } } - + // Make sure this sub-mf won’t get parsed as a top level mf $this->elementPrefixParsed($dt, 'dt'); } @@ -768,10 +768,10 @@ public function parseH(\DOMElement $e) { // Look for img @alt if (($e->tagName == 'img' or $e->tagName == 'area') and $e->getAttribute('alt') != '') throw new Exception($e->getAttribute('alt')); - + if ($e->tagName == 'abbr' and $e->hasAttribute('title')) throw new Exception($e->getAttribute('title')); - + // Look for nested img @alt foreach ($this->xpath->query('./img[count(preceding-sibling::*)+count(following-sibling::*)=0]', $e) as $em) { $emNames = mfNamesFromElement($em, 'h-'); @@ -839,7 +839,7 @@ public function parseH(\DOMElement $e) { // Look for img @src if ($e->tagName == 'a' or $e->tagName == 'area') $url = $e->getAttribute('href'); - + // Look for nested a @href foreach ($this->xpath->query('./a[count(preceding-sibling::a)+count(following-sibling::a)=0]', $e) as $em) { $emNames = mfNamesFromElement($em, 'h-'); @@ -857,14 +857,14 @@ public function parseH(\DOMElement $e) { break; } } - + if (!empty($url)) $return['url'][] = $this->resolveUrl($url); } // Make sure things are in alphabetical order sort($mfTypes); - + // Phew. Return the final result. $parsed = array( 'type' => $mfTypes, @@ -884,11 +884,11 @@ public function parseH(\DOMElement $e) { } return $parsed; } - + /** * Parse Rels and Alternatives - * - * Returns [$rels, $alternatives]. If the $rels value is to be empty, i.e. there are no links on the page + * + * Returns [$rels, $alternatives]. If the $rels value is to be empty, i.e. there are no links on the page * with a rel value *not* containing `alternate`, then the type of $rels depends on $this->jsonMode. If set * to true, it will be a stdClass instance, optimising for JSON serialisation. Otherwise (the default case), * it will be an empty array. @@ -896,18 +896,18 @@ public function parseH(\DOMElement $e) { public function parseRelsAndAlternates() { $rels = array(); $alternates = array(); - + // Iterate through all a, area and link elements with rel attributes foreach ($this->xpath->query('//*[@rel and @href]') as $hyperlink) { if ($hyperlink->getAttribute('rel') == '') continue; - + // Resolve the href $href = $this->resolveUrl($hyperlink->getAttribute('href')); - + // Split up the rel into space-separated values $linkRels = array_filter(explode(' ', $hyperlink->getAttribute('rel'))); - + // If alternate in rels, create alternate structure, append if (in_array('alternate', $linkRels)) { $alt = array( @@ -916,10 +916,19 @@ public function parseRelsAndAlternates() { ); if ($hyperlink->hasAttribute('media')) $alt['media'] = $hyperlink->getAttribute('media'); - + if ($hyperlink->hasAttribute('hreflang')) $alt['hreflang'] = $hyperlink->getAttribute('hreflang'); - + + if ($hyperlink->hasAttribute('title')) + $alt['title'] = $hyperlink->getAttribute('title'); + + if ($hyperlink->hasAttribute('type')) + $alt['type'] = $hyperlink->getAttribute('type'); + + if ($hyperlink->nodeValue) + $alt['text'] = $hyperlink->nodeValue; + $alternates[] = $alt; } else { foreach ($linkRels as $rel) { @@ -927,38 +936,38 @@ public function parseRelsAndAlternates() { } } } - + if (empty($rels) and $this->jsonMode) { $rels = new stdClass(); } - + return array($rels, $alternates); } - + /** * Kicks off the parsing routine - * + * * If `$htmlSafe` is set, any angle brackets in the results from non e-* properties * will be HTML-encoded, bringing all output to the same level of encoding. - * + * * If a DOMElement is set as the $context, only descendants of that element will * be parsed for microformats. - * + * * @param bool $htmlSafe whether or not to html-encode non e-* properties. Defaults to false * @param DOMElement $context optionally an element from which to parse microformats * @return array An array containing all the µfs found in the current document */ public function parse($convertClassic = true, DOMElement $context = null) { $mfs = array(); - + if ($convertClassic) { $this->convertLegacy(); } - + $mfElements = null === $context ? $this->xpath->query('//*[contains(concat(" ", @class), " h-")]') : $this->xpath->query('.//*[contains(concat(" ", @class), " h-")]', $context); - + // Parser microformats foreach ($mfElements as $node) { // For each microformat @@ -967,64 +976,64 @@ public function parse($convertClassic = true, DOMElement $context = null) { // Add the value to the array for this property type $mfs[] = $result; } - + // Parse rels list($rels, $alternates) = $this->parseRelsAndAlternates(); - + $top = array( 'items' => array_values(array_filter($mfs)), 'rels' => $rels ); - + if (count($alternates)) $top['alternates'] = $alternates; - + return $top; } - + /** * Parse From ID - * + * * Given an ID, parse all microformats which are children of the element with * that ID. - * + * * Note that rel values are still document-wide. - * - * If an element with the ID is not found, an empty skeleton mf2 array structure + * + * If an element with the ID is not found, an empty skeleton mf2 array structure * will be returned. - * + * * @param string $id * @param bool $htmlSafe = false whether or not to HTML-encode angle brackets in non e-* properties * @return array */ public function parseFromId($id, $convertClassic=true) { $matches = $this->xpath->query("//*[@id='{$id}']"); - + if (empty($matches)) return array('items' => array(), 'rels' => array(), 'alternates' => array()); - + return $this->parse($convertClassic, $matches->item(0)); } /** * Convert Legacy Classnames - * + * * Adds microformats2 classnames into a document containing only legacy * semantic classnames. - * + * * @return Parser $this */ public function convertLegacy() { $doc = $this->doc; $xp = new DOMXPath($doc); - + // replace all roots foreach ($this->classicRootMap as $old => $new) { foreach ($xp->query('//*[contains(concat(" ", @class, " "), " ' . $old . ' ") and not(contains(concat(" ", @class, " "), " ' . $new . ' "))]') as $el) { $el->setAttribute('class', $el->getAttribute('class') . ' ' . $new); } } - + foreach ($this->classicPropertyMap as $oldRoot => $properties) { $newRoot = $this->classicRootMap[$oldRoot]; foreach ($properties as $old => $new) { @@ -1033,16 +1042,16 @@ public function convertLegacy() { } } } - + return $this; } - + /** * XPath Query - * + * * Runs an XPath query over the current document. Works in exactly the same * way as DOMXPath::query. - * + * * @param string $expression * @param DOMNode $context * @return DOMNodeList @@ -1050,7 +1059,7 @@ public function convertLegacy() { public function query($expression, $context = null) { return $this->xpath->query($expression, $context); } - + /** * Classic Root Classname map */ @@ -1064,7 +1073,7 @@ public function query($expression, $context = null) { 'hreview' => 'h-review', 'hproduct' => 'h-product' ); - + public $classicPropertyMap = array( 'vcard' => array( 'fn' => 'p-name', @@ -1293,7 +1302,7 @@ function resolveUrl($baseURI, $referenceURI) { # 5.2.3 Merge Paths function mergePaths($base, $reference) { # If the base URI has a defined authority component and an empty - # path, + # path, if($base['authority'] && $base['path'] == null) { # then return a string consisting of "/" concatenated with the # reference's path; otherwise, diff --git a/tests/Mf2/ParserTest.php b/tests/Mf2/ParserTest.php index 05475ff..031f73c 100644 --- a/tests/Mf2/ParserTest.php +++ b/tests/Mf2/ParserTest.php @@ -8,10 +8,10 @@ /** * Parser Test - * + * * Contains tests for internal parsing functions and stuff which doesn’t go anywhere else, i.e. * isn’t related to a particular property as such. - * + * * Stuff for parsing E goes in here until there is enough of it to go elsewhere (like, never?) */ class ParserTest extends PHPUnit_Framework_TestCase { @@ -19,13 +19,13 @@ class ParserTest extends PHPUnit_Framework_TestCase { public function setUp() { date_default_timezone_set('Europe/London'); } - + public function testUnicodeTrim() { $this->assertEquals('thing', Mf2\unicodeTrim(' thing ')); $this->assertEquals('thing', Mf2\unicodeTrim(' thing ')); $this->assertEquals('thing', Mf2\unicodeTrim(mb_convert_encoding('   thing   ', 'UTF-8', 'HTML-ENTITIES') )); } - + public function testMicroformatNameFromClassReturnsFullRootName() { $expected = array('h-card'); $actual = Mf2\mfNamesFromClass('someclass h-card someotherclass', 'h-'); @@ -51,7 +51,7 @@ public function testNestedMicroformatPropertyNameWorks() { $expected = array('location', 'author'); $test = 'someclass p-location someotherclass u-author'; $actual = Mf2\nestedMfPropertyNamesFromClass($test); - + $this->assertEquals($expected, $actual); } @@ -76,7 +76,7 @@ public function testMicroformatNamesFromClassIgnoresUppercaseClassnames() { $this->assertEquals($expected, $actual); } - + public function testParseE() { $input = '
Here is a load of embedded markup
'; //$parser = new Parser($input); @@ -86,12 +86,12 @@ public function testParseE() { $this->assertEquals('Here is a load of embedded markup', $output['items'][0]['properties']['content'][0]['html']); $this->assertEquals('Here is a load of embedded markup', $output['items'][0]['properties']['content'][0]['value']); } - + public function testParseEResolvesRelativeLinks() { $input = '

Blah blah thing.

'; $parser = new Parser($input, 'http://example.com'); $output = $parser->parse(); - + $this->assertEquals('Blah blah thing. ', $output['items'][0]['properties']['content'][0]['html']); $this->assertEquals('Blah blah thing. http://example.com/img', $output['items'][0]['properties']['content'][0]['value']); } @@ -110,59 +110,62 @@ public function testInvalidClassnamesContainingHAreIgnored() { $this->fail(); } } - + public function testHtmlSpecialCharactersWorks() { $this->assertEquals('<>', htmlspecialchars('<>')); } - + public function testHtmlEncodesNonEProperties() { $input = '
<p> <dt> <u>
'; - + $parser = new Parser($input); $output = $parser->parse(); - + $this->assertEquals('

', $output['items'][0]['properties']['name'][0]); $this->assertEquals('

', $output['items'][0]['properties']['published'][0]); $this->assertEquals('', $output['items'][0]['properties']['url'][0]); } - + public function testHtmlEncodesImpliedProperties() { $input = '<name>'; $parser = new Parser($input); $output = $parser->parse(); - + $this->assertEquals('', $output['items'][0]['properties']['name'][0]); $this->assertEquals('', $output['items'][0]['properties']['url'][0]); $this->assertEquals('', $output['items'][0]['properties']['photo'][0]); } - + public function testParsesRelValues() { $input = ''; $parser = new Parser($input); - + $output = $parser->parse(); - + $this->assertArrayHasKey('rels', $output); $this->assertEquals('http://example.com', $output['rels']['author'][0]); } - + public function testParsesRelAlternateValues() { - $input = ''; + $input = 'German Homepage'; $parser = new Parser($input); $output = $parser->parse(); - + $this->assertArrayHasKey('alternates', $output); $this->assertEquals('http://example.org', $output['alternates'][0]['url']); $this->assertEquals('home', $output['alternates'][0]['rel']); $this->assertEquals('de', $output['alternates'][0]['hreflang']); $this->assertEquals('screen', $output['alternates'][0]['media']); + $this->assertEquals('text/html', $output['alternates'][0]['type']); + $this->assertEquals('German Homepage', $output['alternates'][0]['title']); + $this->assertEquals('German Homepage', $output['alternates'][0]['text']); } - + public function testParseFromIdOnlyReturnsMicroformatsWithinThatId() { $input = <<Not Included @@ -173,14 +176,14 @@ public function testParseFromIdOnlyReturnsMicroformatsWithinThatId() {
Not Included
EOT; - + $parser = new Parser($input); $output = $parser->parseFromId('parse-here'); - + $this->assertCount(1, $output['items']); $this->assertEquals('Included', $output['items'][0]['properties']['name'][0]); } - + /** * Issue #21 github.com/indieweb/php-mf2/issues/21 */ @@ -190,16 +193,16 @@ public function testDoesntAddArraysWithOnlyValueForAlreadyParsedNestedMicroforma
Nested Author
- + Real Author EOT; $parser = new Parser($input); $output = $parser->parse(); - + $this->assertCount(1, $output['items'][0]['properties']['author']); } - + public function testParsesNestedMicroformatsWithClassnamesInAnyOrder() { $input = << @@ -208,7 +211,7 @@ public function testParsesNestedMicroformatsWithClassnamesInAnyOrder() { EOT; $parser = new Parser($input); $output = $parser->parse(); - + $this->assertCount(1, $output['items'][0]['properties']['in-reply-to']); $this->assertEquals('Name', $output['items'][0]['properties']['in-reply-to'][0]['properties']['name'][0]); } @@ -266,10 +269,10 @@ public function testNotImpliedUrlFromHCard() { John Q EOT; - + $parser = new Parser($input); $output = $parser->parse(); - + $this->assertArrayNotHasKey('url', $output['items'][0]['properties']); } @@ -279,10 +282,10 @@ public function testAreaTag() { Person Bee EOT; - + $parser = new Parser($input); $output = $parser->parse(); - + $this->assertEquals('', $output['items'][0]['properties']['name'][0]); $this->assertEquals('rect', $output['items'][0]['properties']['category'][0]['shape']); $this->assertEquals('100,100,120,120', $output['items'][0]['properties']['category'][0]['coords']);