Skip to content

Commit

Permalink
Merge pull request #125 from gRegorLove/issue112
Browse files Browse the repository at this point in the history
Add rel-urls object
  • Loading branch information
aaronpk committed Jun 11, 2017
2 parents e7c0cac + 20f17d2 commit 06944f0
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 33 deletions.
91 changes: 61 additions & 30 deletions Mf2/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@ class Parser {
/** @var boolean Whether to include experimental language parsing in the result */
public $lang = false;

/** @var bool Whether to include alternates object (dropped from spec in favor of rel-urls) */
public $enableAlternates = false;

/**
* Elements upgraded to mf2 during backcompat
* @var SplObjectStorage
Expand Down Expand Up @@ -1153,62 +1156,89 @@ public function parseImpliedPhoto(\DOMElement $e) {
}

/**
* Parse Rels and Alternatives
* Parse rels and alternates
*
* 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.
* Returns [$rels, $rel_urls, $alternates].
* For $rels and $rel_urls, if they are empty and $this->jsonMode = true, they will be returned as stdClass,
* optimizing for JSON serialization. Otherwise they will be returned as an empty array.
* Note that $alternates is deprecated in the microformats spec in favor of $rel_urls. $alternates only appears
* in parsed results if $this->enableAlternates = true.
* @return array|stdClass
*/
public function parseRelsAndAlternates() {
$rels = array();
$rel_urls = array();
$alternates = array();

// Iterate through all a, area and link elements with rel attributes
foreach ($this->xpath->query('//a[@rel and @href] | //link[@rel and @href] | //area[@rel and @href]') as $hyperlink) {
if ($hyperlink->getAttribute('rel') == '')
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(
'url' => $href,
'rel' => implode(' ', array_diff($linkRels, array('alternate')))
);
if ($hyperlink->hasAttribute('media'))
$alt['media'] = $hyperlink->getAttribute('media');
$rel_attributes = array();

if ($hyperlink->hasAttribute('hreflang'))
$alt['hreflang'] = $hyperlink->getAttribute('hreflang');
if ($hyperlink->hasAttribute('media')) {
$rel_attributes['media'] = $hyperlink->getAttribute('media');
}

if ($hyperlink->hasAttribute('title'))
$alt['title'] = $hyperlink->getAttribute('title');
if ($hyperlink->hasAttribute('hreflang')) {
$rel_attributes['hreflang'] = $hyperlink->getAttribute('hreflang');
}

if ($hyperlink->hasAttribute('type'))
$alt['type'] = $hyperlink->getAttribute('type');
if ($hyperlink->hasAttribute('title')) {
$rel_attributes['title'] = $hyperlink->getAttribute('title');
}

if ($hyperlink->nodeValue)
$alt['text'] = $hyperlink->nodeValue;
if ($hyperlink->hasAttribute('type')) {
$rel_attributes['type'] = $hyperlink->getAttribute('type');
}

$alternates[] = $alt;
} else {
foreach ($linkRels as $rel) {
$rels[$rel][] = $href;
if ($hyperlink->nodeValue) {
$rel_attributes['text'] = $hyperlink->nodeValue;
}

if ($this->enableAlternates) {
// If 'alternate' in rels, create 'alternates' structure, append
if (in_array('alternate', $linkRels)) {
$alternates[] = array_merge(
$rel_attributes,
array(
'url' => $href,
'rel' => implode(' ', array_diff($linkRels, array('alternate')))
)
);
}
}

foreach ($linkRels as $rel) {
$rels[$rel][] = $href;
}

if (!in_array($href, $rel_urls)) {
$rel_urls[$href] = array_merge(
$rel_attributes,
array('rels' => $linkRels)
);
}

}

if (empty($rels) and $this->jsonMode) {
$rels = new stdClass();
}

return array($rels, $alternates);
if (empty($rel_urls) and $this->jsonMode) {
$rel_urls = new stdClass();
}

return array($rels, $rel_urls, $alternates);
}

/**
Expand Down Expand Up @@ -1239,14 +1269,15 @@ public function parse($convertClassic = true, DOMElement $context = null) {
}

// Parse rels
list($rels, $alternates) = $this->parseRelsAndAlternates();
list($rels, $rel_urls, $alternates) = $this->parseRelsAndAlternates();

$top = array(
'items' => array_values(array_filter($mfs)),
'rels' => $rels
'rels' => $rels,
'rel-urls' => $rel_urls,
);

if (count($alternates)) {
if ($this->enableAlternates && count($alternates)) {
$top['alternates'] = $alternates;
}

Expand Down
4 changes: 2 additions & 2 deletions tests/Mf2/ClassicMicroformatsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ public function setUp() {

public function testParsesClassicHcard() {
$input = '<div class="vcard"><span class="fn n">Barnaby Walters</span> is a person.</div>';
$expected = '{"items": [{"type": ["h-card"], "properties": {"name": ["Barnaby Walters"]}}], "rels": {}}';
$expected = '{"items": [{"type": ["h-card"], "properties": {"name": ["Barnaby Walters"]}}], "rels": {}, "rel-urls": {}}';
$parser = new Parser($input, '', true);
$this->assertJsonStringEqualsJsonString(json_encode($parser->parse()), $expected);
}

public function testParsesClassicHEntry() {
$input = '<div class="hentry"><h1 class="entry-title">microformats2 Is Great</h1> <p class="entry-summary">yes yes it is.</p></div>';
$expected = '{"items": [{"type": ["h-entry"], "properties": {"name": ["microformats2 Is Great"], "summary": ["yes yes it is."]}}], "rels": {}}';
$expected = '{"items": [{"type": ["h-entry"], "properties": {"name": ["microformats2 Is Great"], "summary": ["yes yes it is."]}}], "rels": {}, "rel-urls": {}}';
$parser = new Parser($input, '', true);
$this->assertJsonStringEqualsJsonString(json_encode($parser->parse()), $expected);
}
Expand Down
8 changes: 7 additions & 1 deletion tests/Mf2/CombinedMicroformatsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function testHEventLocationHCard() {
</div>';
$expected = '{
"rels": {},
"rel-urls": {},
"items": [{
"type": ["h-event"],
"properties": {
Expand Down Expand Up @@ -79,6 +80,7 @@ public function testHCardOrgPOrg() {
</div>';
$expected = '{
"rels": {},
"rel-urls": {},
"items": [{
"type": ["h-card"],
"properties": {
Expand Down Expand Up @@ -110,6 +112,7 @@ public function testHCardOrgHCard() {
</div>';
$expected = '{
"rels": {},
"rel-urls": {},
"items": [{
"type": ["h-card"],
"properties": {
Expand Down Expand Up @@ -148,6 +151,7 @@ public function testHCardPOrgHCardHOrg() {
</div>';
$expected = '{
"rels": {},
"rel-urls": {},
"items": [{
"type": ["h-card"],
"properties": {
Expand Down Expand Up @@ -184,6 +188,7 @@ public function testHCardChildHCard() {
</div>';
$expected = '{
"rels": {},
"rel-urls": {},
"items": [{
"type": ["h-card"],
"properties": {
Expand Down Expand Up @@ -286,7 +291,8 @@ public function testMicroformatsNestedUnderUPropertyClassnamesDeriveValueFromURL
}]
}
}],
"rels":[]
"rels":[],
"rel-urls": []
}';
$mf = Mf2\parse($input);

Expand Down
7 changes: 7 additions & 0 deletions tests/Mf2/MicroformatsWikiExamplesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public function testHandlesEmptyStringsCorrectly() {
$input = '';
$expected = '{
"rels": {},
"rel-urls": {},
"items": []
}';

Expand All @@ -42,6 +43,7 @@ public function testHandlesNullCorrectly() {
$input = Null;
$expected = '{
"rels": {},
"rel-urls": {},
"items": []
}';

Expand All @@ -59,6 +61,7 @@ public function testSimplePersonReference() {
$input = '<span class="h-card">Frances Berriman</span>';
$expected = '{
"rels": {},
"rel-urls": {},
"items": [{
"type": ["h-card"],
"properties": {
Expand All @@ -79,6 +82,7 @@ public function testSimpleHyperlinkedPersonReference() {
$input = '<a class="h-card" href="http://benward.me">Ben Ward</a>';
$expected = '{
"rels": {},
"rel-urls": {},
"items": [{
"type": ["h-card"],
"properties": {
Expand All @@ -101,6 +105,7 @@ public function testSimplePersonImage() {
// Added root items key
$expected = '{
"rels": {},
"rel-urls": {},
"items": [{
"type": ["h-card"],
"properties": {
Expand All @@ -125,6 +130,7 @@ public function testHyperlinkedImageNameAndPhotoProperties() {
// Added root items key
$expected = '{
"rels": {},
"rel-urls": {},
"items": [{
"type": ["h-card"],
"properties": {
Expand Down Expand Up @@ -162,6 +168,7 @@ public function testMoreDetailedPerson() {

$expected = '{
"rels": {},
"rel-urls": {},
"items": [{
"type": ["h-card"],
"properties": {
Expand Down
1 change: 1 addition & 0 deletions tests/Mf2/ParseImpliedTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ public function testMultipleImpliedHCards() {
</a>';
$expected = '{
"rels": {},
"rel-urls": {},
"items": [{
"type": ["h-card"],
"properties": {
Expand Down
1 change: 1 addition & 0 deletions tests/Mf2/ParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ public function testParsesRelValues() {
public function testParsesRelAlternateValues() {
$input = '<a rel="alternate home" href="http://example.org" hreflang="de", media="screen" type="text/html" title="German Homepage Link">German Homepage</a>';
$parser = new Parser($input);
$parser->enableAlternates = true;
$output = $parser->parse();

$this->assertArrayHasKey('alternates', $output);
Expand Down
82 changes: 82 additions & 0 deletions tests/Mf2/RelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,86 @@ public function testRelValueOnBTag() {
$this->assertArrayNotHasKey('webmention', $output['rels']);
}

public function testEnableAlternatesFlagTrue() {
$input = '<a rel="author" href="http://example.com/a">author a</a>
<a rel="author" href="http://example.com/b">author b</a>
<a rel="in-reply-to" href="http://example.com/1">post 1</a>
<a rel="in-reply-to" href="http://example.com/2">post 2</a>
<a rel="alternate home"
href="http://example.com/fr"
media="handheld"
hreflang="fr">French mobile homepage</a>';
$parser = new Parser($input);
$parser->enableAlternates = true;
$output = $parser->parse();

$this->assertArrayHasKey('alternates', $output);
}

public function testEnableAlternatesFlagFalse() {
$input = '<a rel="author" href="http://example.com/a">author a</a>
<a rel="author" href="http://example.com/b">author b</a>
<a rel="in-reply-to" href="http://example.com/1">post 1</a>
<a rel="in-reply-to" href="http://example.com/2">post 2</a>
<a rel="alternate home"
href="http://example.com/fr"
media="handheld"
hreflang="fr">French mobile homepage</a>';
$parser = new Parser($input);
$parser->enableAlternates = false;
$output = $parser->parse();

$this->assertArrayNotHasKey('alternates', $output);
}

/**
* @see https://github.com/indieweb/php-mf2/issues/112
* @see http://microformats.org/wiki/microformats2-parsing#rel_parse_examples
*/
public function testRelURLs() {
$input = '<a rel="author" href="http://example.com/a">author a</a>
<a rel="author" href="http://example.com/b">author b</a>
<a rel="in-reply-to" href="http://example.com/1">post 1</a>
<a rel="in-reply-to" href="http://example.com/2">post 2</a>
<a rel="alternate home"
href="http://example.com/fr"
media="handheld"
hreflang="fr">French mobile homepage</a>
<link rel="alternate" type="application/atom+xml" href="http://example.com/articles.atom" title="Atom Feed" />';
$parser = new Parser($input);
$output = $parser->parse();

$this->assertArrayHasKey('rels', $output);
$this->assertCount(4, $output['rels']);
$this->assertArrayHasKey('author', $output['rels']);
$this->assertArrayHasKey('in-reply-to', $output['rels']);
$this->assertArrayHasKey('alternate', $output['rels']);
$this->assertArrayHasKey('home', $output['rels']);

$this->assertArrayHasKey('rel-urls', $output);
$this->assertCount(6, $output['rel-urls']);
$this->assertArrayHasKey('http://example.com/a', $output['rel-urls']);
$this->assertArrayHasKey('http://example.com/b', $output['rel-urls']);
$this->assertArrayHasKey('http://example.com/1', $output['rel-urls']);
$this->assertArrayHasKey('http://example.com/2', $output['rel-urls']);
$this->assertArrayHasKey('http://example.com/fr', $output['rel-urls']);
$this->assertArrayHasKey('http://example.com/articles.atom', $output['rel-urls']);

$this->assertArrayHasKey('rels', $output['rel-urls']['http://example.com/a']);
$this->assertArrayHasKey('text', $output['rel-urls']['http://example.com/a']);
$this->assertArrayHasKey('rels', $output['rel-urls']['http://example.com/b']);
$this->assertArrayHasKey('text', $output['rel-urls']['http://example.com/b']);
$this->assertArrayHasKey('rels', $output['rel-urls']['http://example.com/1']);
$this->assertArrayHasKey('text', $output['rel-urls']['http://example.com/1']);
$this->assertArrayHasKey('rels', $output['rel-urls']['http://example.com/2']);
$this->assertArrayHasKey('text', $output['rel-urls']['http://example.com/2']);
$this->assertArrayHasKey('rels', $output['rel-urls']['http://example.com/fr']);
$this->assertArrayHasKey('text', $output['rel-urls']['http://example.com/fr']);
$this->assertArrayHasKey('media', $output['rel-urls']['http://example.com/fr']);
$this->assertArrayHasKey('hreflang', $output['rel-urls']['http://example.com/fr']);
$this->assertArrayHasKey('title', $output['rel-urls']['http://example.com/articles.atom']);
$this->assertArrayHasKey('type', $output['rel-urls']['http://example.com/articles.atom']);
$this->assertArrayHasKey('rels', $output['rel-urls']['http://example.com/articles.atom']);
}

}

0 comments on commit 06944f0

Please sign in to comment.