Skip to content

Add rel-urls object #125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 11, 2017
Merged
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
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']);
}

}