Repository: microformats/php-mf2 (php)

Issue:
hreview backcompat
php-mf2 does not do `hreview` backcompat see: http://microformats.org/wiki/h-review#Parser_Compatibility

# Example

## HTML
```html
    <article class="hreview">
<a class="url" href="http://example.com">URL</a>
    </article>
```

## Current Output

```json
    "items": [
        {
            "type": [
                "h-review"
            ],
            "properties": []
        }
    ]
```
## Expected output

```json
    "items": [
        {
            "type": [
                "h-review"
            ], 
            "properties": {
                "url": [
                    "http://example.com"
                ]
            }
        }
    ]
```

Pull request:
Fix hreview backcompat
Fixes #155 

- rel="self bookmark" -> u-url
- class="reviewer" -> p-author h-card
- fix class="item hproduct", was backcompat parsing as vevent instead of hproduct
- adds unit tests
- comment out a unit test that was not making any assertions or failures.
- fix some syntax/warnings in newer versions of PHPUnit

Patch:
diff --git a/Mf2/Parser.php b/Mf2/Parser.php
index 7ac471e..e049ead 100644
--- a/Mf2/Parser.php
+++ b/Mf2/Parser.php
@@ -1641,12 +1641,31 @@ class Parser {
 						foreach ( $item_and_hproduct as $tempEl ) {
 							if ( !$this->hasRootMf2($tempEl) ) {
 								$this->addMfClasses($tempEl, 'p-item h-product');
-								$this->backcompat($tempEl, 'vevent');
+								$this->backcompat($tempEl, 'hproduct');
 								$this->addUpgraded($tempEl, array('item', 'hproduct'));
 							}
 						}
 					}
 
+					$rel_self_bookmark = $this->xpath->query('.//*[contains(concat(" ", normalize-space(@rel), " "), " self ") and contains(concat(" ", normalize-space(@rel), " "), " bookmark ")]', $el);
+
+					if ( $rel_self_bookmark->length ) {
+						foreach ( $rel_self_bookmark as $tempEl ) {
+							$this->addMfClasses($tempEl, 'u-url');
+							$this->addUpgraded($tempEl, array('self', 'bookmark'));
+						}
+					}
+
+					$reviewer_nodes = $this->xpath->query('.//*[contains(concat(" ", normalize-space(@class), " "), " reviewer ")]', $el);
+
+					if ( $reviewer_nodes->length ) {
+						foreach ( $reviewer_nodes as $tempEl ) {
+							if ( !$this->hasRootMf2($tempEl) ) {
+								$this->addMfClasses($tempEl, 'p-author h-card');
+							}
+						}
+					}
+
 					$this->upgradeRelTagToCategory($el);
 				break;
 
@@ -2061,10 +2080,7 @@ class Parser {
 				'replace' => 'p-item h-item',
 				'context' => 'item'
 			),
-			'reviewer' => array(
-				'replace' => 'p-author h-card',
-				'context' => 'vcard',
-			),
+			# reviewer: see backcompat()
 			'dtreviewed' => array(
 				'replace' => 'dt-published'
 			),


Test patch:
diff --git a/tests/Mf2/ClassicMicroformatsTest.php b/tests/Mf2/ClassicMicroformatsTest.php
index 5b192c3..d84c0f2 100644
--- a/tests/Mf2/ClassicMicroformatsTest.php
+++ b/tests/Mf2/ClassicMicroformatsTest.php
@@ -113,10 +113,16 @@ EOT;
 		$this->assertContains('txjs', $e['properties']['category']);
 	}
 
-	public function testParsesSnarfedOrgArticleCorrectly() {
+	/**
+	 * This test appears to have never made assertions. When initially
+	 * added it was only printing out the parsed results, probably for
+	 * debugging. Left for reference in case assertions need to be added.
+	 * @see https://github.com/microformats/php-mf2/commit/5fd2c961447f305f50f73e17bd8decf7ec77fa1d#diff-45371c4a595b936f718ab44eb3ff35c326e00a01f2f5cb3d327f34d03750b872
+	 */
+	/*public function testParsesSnarfedOrgArticleCorrectly() {
 		$input = file_get_contents(__DIR__ . '/snarfed.org.html');
 		$result = Mf2\parse($input, 'http://snarfed.org/2013-10-23_oauth-dropins');
-	}
+	}*/
 
 	public function testParsesHProduct() {
 		$input = <<<'EOT'
@@ -761,6 +767,7 @@ END;
 	</div>
 	<p>Visit date: <span>April 2005</span></p>
 	<p>Food eaten: <span>Florentine crepe</span></p>
+	<p>Permanent link for review: <a rel="self bookmark" href="http://example.com/crepe">http://example.com/crepe</a></p>
 </div>
 END;
 		$parser = new Parser($input);
@@ -946,6 +953,54 @@ Two perfectly poached eggs and a thin slice of tasty, French ham rest on a circl
 		$this->assertContains('Garçon', $output['items'][0]['properties']['category']);
 	}
 
+	public function testHReviewItemVevent()
+	{
+		$input = '<div class="hreview">
+		<span><span class="rating">5</span> out of 5 stars</span>
+		<span class="item vevent">
+				<span class="summary">IndieWebCamp 2014</span> -
+				<a href="https://indieweb.org/2014" class="url">indieweb.org/2014</a>
+		</span>
+</div>';
+		$parser = new Parser($input);
+		$output = $parser->parse();
+
+		$this->assertArrayHasKey('item', $output['items'][0]['properties']);
+
+		# assert item type is h-event
+		$this->assertCount(1, $output['items'][0]['properties']['item'][0]['type']);
+		$this->assertEquals('h-event', $output['items'][0]['properties']['item'][0]['type'][0]);
+
+		# assert expected h-event properties
+		$properties = $output['items'][0]['properties']['item'][0]['properties'];
+		$this->assertArrayHasKey('name', $properties);
+		$this->assertArrayHasKey('url', $properties);
+	}
+
+	public function testHReviewItemHproduct()
+	{
+		$input = '<div class="hreview">
+	<span><span class="rating">4</span> out of 5 stars</span>
+	<span class="item hproduct">
+		<span class="fn">Widget</span> -
+		<a href="https://example.com/widget/" class="url">example.com/widget/</a>
+	</span>
+</div>';
+		$parser = new Parser($input);
+		$output = $parser->parse();
+
+		$this->assertArrayHasKey('item', $output['items'][0]['properties']);
+
+		# assert item type is h-product
+		$this->assertCount(1, $output['items'][0]['properties']['item'][0]['type']);
+		$this->assertEquals('h-product', $output['items'][0]['properties']['item'][0]['type'][0]);
+
+		# assert expected h-product properties
+		$properties = $output['items'][0]['properties']['item'][0]['properties'];
+		$this->assertArrayHasKey('name', $properties);
+		$this->assertArrayHasKey('url', $properties);
+	}
+
 	/**
 	 * Should return the last non-empty URL segment
 	 * @see https://github.com/indieweb/php-mf2/issues/157
diff --git a/tests/Mf2/ParserTest.php b/tests/Mf2/ParserTest.php
index dcbedb0..c7b2ef0 100644
--- a/tests/Mf2/ParserTest.php
+++ b/tests/Mf2/ParserTest.php
@@ -139,15 +139,21 @@ class ParserTest extends TestCase {
 	 * @group parseH
 	 */
 	public function testInvalidClassnamesContainingHAreIgnored() {
-		$input = '<div class="asdfgh-jkl"></div>';
+		$classname = 'asdfgh-jkl';
+		$input = sprintf('<div class="%s"></div>', $classname);
 		$parser = new Parser($input);
 		$output = $parser->parse();
 
-		// Look through $output for an item which indicate failure
+		// Look through parsed items for `type` matching the classname.
+		// There shouldn't be any
+		$matches = 0;
 		foreach ($output['items'] as $item) {
-			if (in_array('asdfgh-jkl', $item['type']))
-				$this->fail();
+			if (in_array($classname, $item['type'])) {
+				$matches++;
+			}
 		}
+
+		$this->assertEquals(0, $matches, sprintf('Class name "%s" should not have parsed as a root microformat', $classname));
 	}
 
 	public function testHtmlSpecialCharactersWorks() {
diff --git a/tests/Mf2/URLTest.php b/tests/Mf2/URLTest.php
index c670ba1..32d49ca 100644
--- a/tests/Mf2/URLTest.php
+++ b/tests/Mf2/URLTest.php
@@ -9,7 +9,7 @@ namespace Mf2\Parser\Test;
 use Mf2;
 use Yoast\PHPUnitPolyfills\TestCases\TestCase;
 
-class UrlTest extends TestCase {
+class URLTest extends TestCase {
 	protected function set_up() {
 		date_default_timezone_set('Europe/London');
 	}
@@ -149,7 +149,7 @@ class UrlTest extends TestCase {
 	}
 
 	/**
-	 * @dataProvider testData
+	 * @dataProvider dataProvider
 	 */
   public function testReturnsUrlIfAbsolute($assert, $base, $url, $expected) {
     $actual = mf2\resolveUrl($base, $url);
@@ -157,7 +157,7 @@ class UrlTest extends TestCase {
     $this->assertEquals($expected, $actual, $assert);
   }
 
-	public function testData() {
+	public function dataProvider() {
 		// seriously, please update to PHP 5.4 so I can use nice array syntax ;)
 		// fail message, base, url, expected
 		$cases = array(