From 78e3e16592b75637c9ed9c3f8fe0f7b99e8638c2 Mon Sep 17 00:00:00 2001 From: Aaron Parecki Date: Sat, 29 Apr 2017 12:33:02 -0700 Subject: [PATCH] finishes the refactor! --- README.md | 55 +++++++++++++- composer.lock | 10 +-- controllers/Parse.php | 151 +++++--------------------------------- lib/XRay/Fetcher.php | 9 ++- lib/XRay/Formats/HTML.php | 132 +++++++++++++++++++++++++++++++++ lib/XRay/Formats/Mf2.php | 45 ++++++------ lib/XRay/Parser.php | 7 +- tests/ParseTest.php | 51 ++++++------- 8 files changed, 267 insertions(+), 193 deletions(-) create mode 100644 lib/XRay/Formats/HTML.php diff --git a/README.md b/README.md index dca7161..d688513 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,53 @@ The contents of the URL is checked in the following order: * h-recipe * h-product -## Parse API +## Library + +XRay can be used as a library in your PHP project. The easiest way to install it and its dependencies is via composer. + +``` +composer require p3k/xray +``` + +Basic usage: + +```php +$xray = new p3k\XRay(); +$parsed = $xray->parse('https://aaronparecki.com/2017/04/28/9/'); +``` + +If you already have an HTML or JSON document you want to parse, you can pass it as a string in the second parameter. + +```php +$xray = new p3k\XRay(); +$html = '....'; +$parsed = $xray->parse('https://aaronparecki.com/2017/04/28/9/', $html); +``` + +In both cases, you can add an additional parameter to configure various options of how XRay will behave. Below is a list of the options. + +* `timeout` - The timeout in seconds to wait for any HTTP requests +* `max_redirects` - The maximum number of redirects to follow +* `include_original` - Will also return the full document fetched +* `target` - Specify a target URL, and XRay will first check if that URL is on the page, and only if it is, will continue to parse the page. This is useful when you're using XRay to verify an incoming webmention. + +Additionally, the following parameters are supported when making requests that use the Twitter or GitHub API. See the authentication section below for details. + +```php +$xray = new p3k\XRay(); + +$parsed = $xray->parse('https://aaronparecki.com/2017/04/28/9/', [ + 'timeout' => 30 +]); + +$parsed = $xray->parse('https://aaronparecki.com/2017/04/28/9/', $html, [ + 'target' => 'http://example.com/' +]); +``` + +## API + +XRay can also be used as an API to provide its parsing capabilities over an HTTP service. To parse a page and return structured data for the contents of the page, simply pass a url to the parse route. @@ -84,6 +130,13 @@ You should only send Twitter credentials when the URL you are trying to parse is * twitter_access_token_secret - Your Twitter secret access token +### GitHub Authentication + +XRay uses the GitHub API to fetch GitHub URLs, which provides higher rate limits when used with authentication. You can pass a GitHub access token along with the request and XRay will use it when making requests to the API. + +* github_access_token - A GitHub access token + + ### Error Response ```json diff --git a/composer.lock b/composer.lock index 645eae6..10303b9 100644 --- a/composer.lock +++ b/composer.lock @@ -256,16 +256,16 @@ }, { "name": "p3k/http", - "version": "0.1.4", + "version": "0.1.5", "source": { "type": "git", "url": "https://github.com/aaronpk/p3k-http.git", - "reference": "136aac6f7ecd6d6e16e8ff9286b43110680c49ab" + "reference": "3740fe135e6d58457d7528e7c05a67b68e020a79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aaronpk/p3k-http/zipball/136aac6f7ecd6d6e16e8ff9286b43110680c49ab", - "reference": "136aac6f7ecd6d6e16e8ff9286b43110680c49ab", + "url": "https://api.github.com/repos/aaronpk/p3k-http/zipball/3740fe135e6d58457d7528e7c05a67b68e020a79", + "reference": "3740fe135e6d58457d7528e7c05a67b68e020a79", "shasum": "" }, "require": { @@ -290,7 +290,7 @@ ], "description": "A simple wrapper API around the PHP curl functions", "homepage": "https://github.com/aaronpk/p3k-http", - "time": "2017-04-28T19:46:12+00:00" + "time": "2017-04-29T17:43:29+00:00" }, { "name": "p3k/timezone", diff --git a/controllers/Parse.php b/controllers/Parse.php index 44a8b34..dbc05e4 100644 --- a/controllers/Parse.php +++ b/controllers/Parse.php @@ -41,10 +41,6 @@ class Parse { return $response; } - private static function toHtmlEntities($input) { - return mb_convert_encoding($input, 'HTML-ENTITIES', mb_detect_encoding($input)); - } - public function parse(Request $request, Response $response) { $opts = []; @@ -57,6 +53,10 @@ class Parse { $opts['max_redirects'] = (int)$request->get('max_redirects'); } + if($request->get('target')) { + $opts['target'] = $request->get('target'); + } + if($request->get('pretty')) { $this->_pretty = true; } @@ -105,136 +105,23 @@ class Parse { if(isset($parsed['code'])) $result['code'] = $parsed['code']; - $data = [ - 'data' => $parsed['data'], - 'url' => $result['url'], - 'code' => $result['code'] - ]; - if($request->get('include_original') && isset($parsed['original'])) - $data['original'] = $parsed['original']; - - return $this->respond($response, 200, $data); - - - - - // attempt to parse the page as HTML - $doc = new DOMDocument(); - @$doc->loadHTML(self::toHtmlEntities($result['body'])); - - if(!$doc) { - return $this->respond($response, 200, [ - 'error' => 'invalid_content', - 'error_description' => 'The document could not be parsed as HTML' - ]); - } - - $xpath = new DOMXPath($doc); - - // Check for meta http equiv and replace the status code if present - foreach($xpath->query('//meta[translate(@http-equiv,\'STATUS\',\'status\')=\'status\']') as $el) { - $equivStatus = ''.$el->getAttribute('content'); - if($equivStatus && is_string($equivStatus)) { - if(preg_match('/^(\d+)/', $equivStatus, $match)) { - $result['code'] = (int)$match[1]; - } - } - } - - // If a target parameter was provided, make sure a link to it exists on the page - if($target=$request->get('target')) { - $found = []; - if($target) { - self::xPathFindNodeWithAttribute($xpath, 'a', 'href', function($u) use($target, &$found){ - if($u == $target) { - $found[$u] = null; - } - }); - self::xPathFindNodeWithAttribute($xpath, 'img', 'src', function($u) use($target, &$found){ - if($u == $target) { - $found[$u] = null; - } - }); - self::xPathFindNodeWithAttribute($xpath, 'video', 'src', function($u) use($target, &$found){ - if($u == $target) { - $found[$u] = null; - } - }); - self::xPathFindNodeWithAttribute($xpath, 'audio', 'src', function($u) use($target, &$found){ - if($u == $target) { - $found[$u] = null; - } - }); - } - - if(!$found) { - return $this->respond($response, 200, [ - 'error' => 'no_link_found', - 'error_description' => 'The source document does not have a link to the target URL', - 'url' => $result['url'], - 'code' => $result['code'], - ]); - } - } - - // If the URL has a fragment ID, find the DOM starting at that node and parse it instead - $html = $result['body']; - - $fragment = parse_url($url, PHP_URL_FRAGMENT); - if($fragment) { - $fragElement = self::xPathGetElementById($xpath, $fragment); - if($fragElement) { - $html = $doc->saveHTML($fragElement); - $foundFragment = true; - } else { - $foundFragment = false; - } - } - - // Now start pulling in the data from the page. Start by looking for microformats2 - $mf2 = mf2\Parse($html, $result['url']); - - if($mf2 && count($mf2['items']) > 0) { - $data = Formats\Mf2::parse($mf2, $result['url'], $this->http); - if($data) { - if($fragment) { - $data['info'] = [ - 'found_fragment' => $foundFragment - ]; - } - if($request->get('include_original')) - $data['original'] = $html; - $data['url'] = $result['url']; // this will be the effective URL after following redirects - $data['code'] = $result['code']; - return $this->respond($response, 200, $data); - } - } - - // TODO: look for other content like OEmbed or other known services later - - return $this->respond($response, 200, [ - 'data' => [ - 'type' => 'unknown', - ], - 'url' => $result['url'], - 'code' => $result['code'] - ]); - } - - private static function xPathFindNodeWithAttribute($xpath, $node, $attr, $callback) { - foreach($xpath->query('//'.$node.'[@'.$attr.']') as $el) { - $v = $el->getAttribute($attr); - $callback($v); - } - } + if(!empty($parsed['error'])) { + $error_code = isset($parsed['error_code']) ? $parsed['error_code'] : 200; + unset($parsed['error_code']); + return $this->respond($response, $error_code, $parsed); + } else { + $data = [ + 'data' => $parsed['data'], + 'url' => $result['url'], + 'code' => $result['code'] + ]; + if(isset($parsed['info'])) + $data['info'] = $parsed['info']; + if($request->get('include_original') && isset($parsed['original'])) + $data['original'] = $parsed['original']; - private static function xPathGetElementById($xpath, $id) { - $element = null; - foreach($xpath->query("//*[@id='$id']") as $el) { - $element = $el; + return $this->respond($response, 200, $data); } - return $element; } - } diff --git a/lib/XRay/Fetcher.php b/lib/XRay/Fetcher.php index 1ead5e4..8139cf8 100644 --- a/lib/XRay/Fetcher.php +++ b/lib/XRay/Fetcher.php @@ -78,7 +78,8 @@ class Fetcher { if($result['code'] == 410) { // 410 Gone responses are valid and should not return an error return $this->respond($response, 200, [ - 'TODO' => [ + 'data' => [ + 'type' => 'unknown' ], 'url' => $result['url'], 'code' => $result['code'] @@ -111,6 +112,11 @@ class Fetcher { ]; } + // If the original URL had a fragment, include it in the final URL + if(($fragment=parse_url($url, PHP_URL_FRAGMENT)) && !parse_url($result['url'], PHP_URL_FRAGMENT)) { + $result['url'] .= '#'.$fragment; + } + return [ 'url' => $result['url'], 'body' => $result['body'], @@ -127,7 +133,6 @@ class Fetcher { } if(count($creds) < 4) { -print_r(debug_backtrace()[1]); return [ 'error_code' => 400, 'error' => 'missing_parameters', diff --git a/lib/XRay/Formats/HTML.php b/lib/XRay/Formats/HTML.php new file mode 100644 index 0000000..45c6c45 --- /dev/null +++ b/lib/XRay/Formats/HTML.php @@ -0,0 +1,132 @@ + [ + 'type' => 'unknown', + ], + 'url' => $url, + ]; + + // attempt to parse the page as HTML + $doc = new DOMDocument(); + @$doc->loadHTML(self::toHtmlEntities($html)); + + if(!$doc) { + return [ + 'error' => 'invalid_content', + 'error_description' => 'The document could not be parsed as HTML' + ]; + } + + $xpath = new DOMXPath($doc); + + // Check for meta http equiv and replace the status code if present + foreach($xpath->query('//meta[translate(@http-equiv,\'STATUS\',\'status\')=\'status\']') as $el) { + $equivStatus = ''.$el->getAttribute('content'); + if($equivStatus && is_string($equivStatus)) { + if(preg_match('/^(\d+)/', $equivStatus, $match)) { + $result['code'] = (int)$match[1]; + } + } + } + + // If a target parameter was provided, make sure a link to it exists on the page + if(isset($opts['target'])) { + $target = $opts['target']; + + $found = []; + if($target) { + self::xPathFindNodeWithAttribute($xpath, 'a', 'href', function($u) use($target, &$found){ + if($u == $target) { + $found[$u] = null; + } + }); + self::xPathFindNodeWithAttribute($xpath, 'img', 'src', function($u) use($target, &$found){ + if($u == $target) { + $found[$u] = null; + } + }); + self::xPathFindNodeWithAttribute($xpath, 'video', 'src', function($u) use($target, &$found){ + if($u == $target) { + $found[$u] = null; + } + }); + self::xPathFindNodeWithAttribute($xpath, 'audio', 'src', function($u) use($target, &$found){ + if($u == $target) { + $found[$u] = null; + } + }); + } + + if(!$found) { + return [ + 'error' => 'no_link_found', + 'error_description' => 'The source document does not have a link to the target URL', + 'code' => isset($result['code']) ? $result['code'] : 200, + 'url' => $url + ]; + } + } + + // If the URL has a fragment ID, find the DOM starting at that node and parse it instead + $fragment = parse_url($url, PHP_URL_FRAGMENT); + if($fragment) { + $fragElement = self::xPathGetElementById($xpath, $fragment); + if($fragElement) { + $html = $doc->saveHTML($fragElement); + $foundFragment = true; + } else { + $foundFragment = false; + } + } + + // Now start pulling in the data from the page. Start by looking for microformats2 + $mf2 = \mf2\Parse($html, $url); + + if($mf2 && count($mf2['items']) > 0) { + $data = Formats\Mf2::parse($mf2, $url, $http); + $result = array_merge($result, $data); + if($data) { + if($fragment) { + $result['info'] = [ + 'found_fragment' => $foundFragment + ]; + } + $result['original'] = $html; + $result['url'] = $url; // this will be the effective URL after following redirects + } + } + return $result; + } + + private static function toHtmlEntities($input) { + return mb_convert_encoding($input, 'HTML-ENTITIES', mb_detect_encoding($input)); + } + + private static function xPathFindNodeWithAttribute($xpath, $node, $attr, $callback) { + foreach($xpath->query('//'.$node.'[@'.$attr.']') as $el) { + $v = $el->getAttribute($attr); + $callback($v); + } + } + + private static function xPathGetElementById($xpath, $id) { + $element = null; + foreach($xpath->query("//*[@id='$id']") as $el) { + $element = $el; + } + return $element; + } + +} diff --git a/lib/XRay/Formats/Mf2.php b/lib/XRay/Formats/Mf2.php index 93f1890..9bf6605 100644 --- a/lib/XRay/Formats/Mf2.php +++ b/lib/XRay/Formats/Mf2.php @@ -2,7 +2,6 @@ namespace p3k\XRay\Formats; use HTMLPurifier, HTMLPurifier_Config; -use Parse; class Mf2 { @@ -14,31 +13,31 @@ class Mf2 { if(count($mf2['items']) == 1) { $item = $mf2['items'][0]; if(in_array('h-entry', $item['type']) || in_array('h-cite', $item['type'])) { - Parse::debug("mf2:0: Recognized $url as an h-entry it is the only item on the page"); + #Parse::debug("mf2:0: Recognized $url as an h-entry it is the only item on the page"); return self::parseAsHEntry($mf2, $item, $http); } if(in_array('h-event', $item['type'])) { - Parse::debug("mf2:0: Recognized $url as an h-event it is the only item on the page"); + #Parse::debug("mf2:0: Recognized $url as an h-event it is the only item on the page"); return self::parseAsHEvent($mf2, $item, $http); } if(in_array('h-review', $item['type'])) { - Parse::debug("mf2:0: Recognized $url as an h-review it is the only item on the page"); + #Parse::debug("mf2:0: Recognized $url as an h-review it is the only item on the page"); return self::parseAsHReview($mf2, $item, $http); } if(in_array('h-recipe', $item['type'])) { - Parse::debug("mf2:0: Recognized $url as an h-recipe it is the only item on the page"); + #Parse::debug("mf2:0: Recognized $url as an h-recipe it is the only item on the page"); return self::parseAsHRecipe($mf2, $item, $http); } if(in_array('h-product', $item['type'])) { - Parse::debug("mf2:0: Recognized $url as an h-product it is the only item on the page"); + #Parse::debug("mf2:0: Recognized $url as an h-product it is the only item on the page"); return self::parseAsHProduct($mf2, $item, $http); } if(in_array('h-feed', $item['type'])) { - Parse::debug("mf2:0: Recognized $url as an h-feed because it is the only item on the page"); + #Parse::debug("mf2:0: Recognized $url as an h-feed because it is the only item on the page"); return self::parseAsHFeed($mf2, $http); } if(in_array('h-card', $item['type'])) { - Parse::debug("mf2:0: Recognized $url as an h-card it is the only item on the page"); + #Parse::debug("mf2:0: Recognized $url as an h-card it is the only item on the page"); return self::parseAsHCard($item, $http, $url); } } @@ -50,7 +49,7 @@ class Mf2 { $urls = $item['properties']['url']; $urls = array_map('\p3k\XRay\normalize_url', $urls); if(in_array($url, $urls)) { - Parse::debug("mf2:1: Recognized $url as a permalink because an object on the page matched the URL of the request"); + #Parse::debug("mf2:1: Recognized $url as a permalink because an object on the page matched the URL of the request"); if(in_array('h-card', $item['type'])) { return self::parseAsHCard($item, $http, $url); } elseif(in_array('h-entry', $item['type']) || in_array('h-cite', $item['type'])) { @@ -64,7 +63,7 @@ class Mf2 { } elseif(in_array('h-product', $item['type'])) { return self::parseAsHProduct($mf2, $item, $http); } else { - Parse::debug('This object was not a recognized type.'); + #Parse::debug('This object was not a recognized type.'); return false; } } @@ -106,7 +105,7 @@ class Mf2 { if(count(array_filter($mf2['items'], function($item){ return in_array('h-entry', $item['type']); })) > 1) { - Parse::debug("mf2:2: Recognized $url as an h-feed because there are more than one object on the page"); + #Parse::debug("mf2:2: Recognized $url as an h-feed because there are more than one object on the page"); return self::parseAsHFeed($mf2, $http); } } @@ -114,7 +113,7 @@ class Mf2 { // If the first item is an h-feed, parse as a feed $first = $mf2['items'][0]; if(in_array('h-feed', $first['type'])) { - Parse::debug("mf2:3: Recognized $url as an h-feed because the first item is an h-feed"); + #Parse::debug("mf2:3: Recognized $url as an h-feed because the first item is an h-feed"); return self::parseAsHFeed($mf2, $http); } @@ -122,24 +121,24 @@ class Mf2 { foreach($mf2['items'] as $item) { // Otherwise check for a recognized h-entr* object if(in_array('h-entry', $item['type']) || in_array('h-cite', $item['type'])) { - Parse::debug("mf2:6: $url is falling back to the first h-entry on the page"); + #Parse::debug("mf2:6: $url is falling back to the first h-entry on the page"); return self::parseAsHEntry($mf2, $item, $http); } elseif(in_array('h-event', $item['type'])) { - Parse::debug("mf2:6: $url is falling back to the first h-event on the page"); + #Parse::debug("mf2:6: $url is falling back to the first h-event on the page"); return self::parseAsHEvent($mf2, $item, $http); } elseif(in_array('h-review', $item['type'])) { - Parse::debug("mf2:6: $url is falling back to the first h-review on the page"); + #Parse::debug("mf2:6: $url is falling back to the first h-review on the page"); return self::parseAsHReview($mf2, $item, $http); } elseif(in_array('h-recipe', $item['type'])) { - Parse::debug("mf2:6: $url is falling back to the first h-recipe on the page"); + #Parse::debug("mf2:6: $url is falling back to the first h-recipe on the page"); return self::parseAsHReview($mf2, $item, $http); } elseif(in_array('h-product', $item['type'])) { - Parse::debug("mf2:6: $url is falling back to the first h-product on the page"); + #Parse::debug("mf2:6: $url is falling back to the first h-product on the page"); return self::parseAsHProduct($mf2, $item, $http); } } - Parse::debug("mf2:E: No object at $url was recognized"); + #Parse::debug("mf2:E: No object at $url was recognized"); return false; } @@ -311,7 +310,7 @@ class Mf2 { ]; if(count($refs)) { - $response['refs'] = $refs; + $response['data']['refs'] = $refs; } return $response; @@ -345,7 +344,7 @@ class Mf2 { ]; if(count($refs)) { - $response['refs'] = $refs; + $response['data']['refs'] = $refs; } return $response; @@ -376,7 +375,7 @@ class Mf2 { ]; if(count($refs)) { - $response['refs'] = $refs; + $response['data']['refs'] = $refs; } return $response; @@ -403,7 +402,7 @@ class Mf2 { ]; if(count($refs)) { - $response['refs'] = $refs; + $response['data']['refs'] = $refs; } return $response; @@ -457,7 +456,7 @@ class Mf2 { ]; if(count($refs)) { - $response['refs'] = $refs; + $response['data']['refs'] = $refs; } return $response; diff --git a/lib/XRay/Parser.php b/lib/XRay/Parser.php index 622e12e..639aba7 100644 --- a/lib/XRay/Parser.php +++ b/lib/XRay/Parser.php @@ -34,11 +34,8 @@ class Parser { return Formats\XKCD::parse($body, $url); } - return [ - 'data' => [ - 'type' => 'unknown' - ] - ]; + // No special parsers matched, parse for Microformats now + return Formats\HTML::parse($this->http, $body, $url, $opts); } } diff --git a/tests/ParseTest.php b/tests/ParseTest.php index d4e45eb..56e3724 100644 --- a/tests/ParseTest.php +++ b/tests/ParseTest.php @@ -205,9 +205,9 @@ class ParseTest extends PHPUnit_Framework_TestCase { $data = json_decode($body, true); $this->assertEquals('entry', $data['data']['type']); $this->assertEquals('http://example.com/100', $data['data']['in-reply-to'][0]); - $this->assertArrayHasKey('http://example.com/100', $data['refs']); - $this->assertEquals('Example Post', $data['refs']['http://example.com/100']['name']); - $this->assertEquals('http://example.com/100', $data['refs']['http://example.com/100']['url']); + $this->assertArrayHasKey('http://example.com/100', $data['data']['refs']); + $this->assertEquals('Example Post', $data['data']['refs']['http://example.com/100']['name']); + $this->assertEquals('http://example.com/100', $data['data']['refs']['http://example.com/100']['url']); } public function testPersonTagIsURL() { @@ -230,10 +230,10 @@ class ParseTest extends PHPUnit_Framework_TestCase { $data = json_decode($body, true); $this->assertEquals('entry', $data['data']['type']); $this->assertEquals('http://alice.example.com/', $data['data']['category'][0]); - $this->assertArrayHasKey('http://alice.example.com/', $data['refs']); - $this->assertEquals('card', $data['refs']['http://alice.example.com/']['type']); - $this->assertEquals('http://alice.example.com/', $data['refs']['http://alice.example.com/']['url']); - $this->assertEquals('Alice', $data['refs']['http://alice.example.com/']['name']); + $this->assertArrayHasKey('http://alice.example.com/', $data['data']['refs']); + $this->assertEquals('card', $data['data']['refs']['http://alice.example.com/']['type']); + $this->assertEquals('http://alice.example.com/', $data['data']['refs']['http://alice.example.com/']['url']); + $this->assertEquals('Alice', $data['data']['refs']['http://alice.example.com/']['name']); } public function testSyndicationIsURL() { @@ -372,10 +372,10 @@ class ParseTest extends PHPUnit_Framework_TestCase { $this->assertEquals($url, $data['data']['url']); $this->assertEquals('2016-02-09T18:30', $data['data']['start']); $this->assertEquals('2016-02-09T19:30', $data['data']['end']); - $this->assertArrayHasKey('http://source.example.com/venue', $data['refs']); - $this->assertEquals('card', $data['refs']['http://source.example.com/venue']['type']); - $this->assertEquals('http://source.example.com/venue', $data['refs']['http://source.example.com/venue']['url']); - $this->assertEquals('Venue', $data['refs']['http://source.example.com/venue']['name']); + $this->assertArrayHasKey('http://source.example.com/venue', $data['data']['refs']); + $this->assertEquals('card', $data['data']['refs']['http://source.example.com/venue']['type']); + $this->assertEquals('http://source.example.com/venue', $data['data']['refs']['http://source.example.com/venue']['url']); + $this->assertEquals('Venue', $data['data']['refs']['http://source.example.com/venue']['name']); } public function testMf2ReviewOfProduct() { @@ -395,10 +395,10 @@ class ParseTest extends PHPUnit_Framework_TestCase { $this->assertContains('red', $data['data']['category']); $this->assertContains('blue', $data['data']['category']); $this->assertContains('http://product.example.com/', $data['data']['item']); - $this->assertArrayHasKey('http://product.example.com/', $data['refs']); - $this->assertEquals('product', $data['refs']['http://product.example.com/']['type']); - $this->assertEquals('The Reviewed Product', $data['refs']['http://product.example.com/']['name']); - $this->assertEquals('http://product.example.com/', $data['refs']['http://product.example.com/']['url']); + $this->assertArrayHasKey('http://product.example.com/', $data['data']['refs']); + $this->assertEquals('product', $data['data']['refs']['http://product.example.com/']['type']); + $this->assertEquals('The Reviewed Product', $data['data']['refs']['http://product.example.com/']['name']); + $this->assertEquals('http://product.example.com/', $data['data']['refs']['http://product.example.com/']['url']); } public function testMf2ReviewOfHCard() { @@ -416,10 +416,10 @@ class ParseTest extends PHPUnit_Framework_TestCase { $this->assertEquals('5', $data['data']['best']); $this->assertEquals('This is the full text of the review', $data['data']['content']['text']); $this->assertContains('http://business.example.com/', $data['data']['item']); - $this->assertArrayHasKey('http://business.example.com/', $data['refs']); - $this->assertEquals('card', $data['refs']['http://business.example.com/']['type']); - $this->assertEquals('The Reviewed Business', $data['refs']['http://business.example.com/']['name']); - $this->assertEquals('http://business.example.com/', $data['refs']['http://business.example.com/']['url']); + $this->assertArrayHasKey('http://business.example.com/', $data['data']['refs']); + $this->assertEquals('card', $data['data']['refs']['http://business.example.com/']['type']); + $this->assertEquals('The Reviewed Business', $data['data']['refs']['http://business.example.com/']['name']); + $this->assertEquals('http://business.example.com/', $data['data']['refs']['http://business.example.com/']['url']); } public function testMf1Review() { @@ -438,10 +438,10 @@ class ParseTest extends PHPUnit_Framework_TestCase { $this->assertEquals('5', $data['data']['best']); $this->assertEquals('This is the full text of the review', $data['data']['content']['text']); // $this->assertContains('http://product.example.com/', $data['data']['item']); - // $this->assertArrayHasKey('http://product.example.com/', $data['refs']); - // $this->assertEquals('product', $data['refs']['http://product.example.com/']['type']); - // $this->assertEquals('The Reviewed Product', $data['refs']['http://product.example.com/']['name']); - // $this->assertEquals('http://product.example.com/', $data['refs']['http://product.example.com/']['url']); + // $this->assertArrayHasKey('http://product.example.com/', $data['data']['refs']); + // $this->assertEquals('product', $data['data']['refs']['http://product.example.com/']['type']); + // $this->assertEquals('The Reviewed Product', $data['data']['refs']['http://product.example.com/']['name']); + // $this->assertEquals('http://product.example.com/', $data['data']['refs']['http://product.example.com/']['url']); } @@ -473,8 +473,8 @@ class ParseTest extends PHPUnit_Framework_TestCase { $this->assertEquals('entry', $data['data']['type']); $this->assertEquals('https://www.facebook.com/555707837940351#tantek', $data['data']['url']); $this->assertContains('https://www.facebook.com/tantek.celik', $data['data']['invitee']); - $this->assertArrayHasKey('https://www.facebook.com/tantek.celik', $data['refs']); - $this->assertEquals('Tantek Çelik', $data['refs']['https://www.facebook.com/tantek.celik']['name']); + $this->assertArrayHasKey('https://www.facebook.com/tantek.celik', $data['data']['refs']); + $this->assertEquals('Tantek Çelik', $data['data']['refs']['https://www.facebook.com/tantek.celik']['name']); } public function testEntryAtFragmentID() { @@ -485,6 +485,7 @@ class ParseTest extends PHPUnit_Framework_TestCase { $this->assertEquals(200, $response->getStatusCode()); $data = json_decode($body, true); $this->assertEquals('entry', $data['data']['type']); + $this->assertEquals('Comment text', $data['data']['content']['text']); $this->assertEquals('http://source.example.com/fragment-id#comment-1000', $data['data']['url']); $this->assertTrue($data['info']['found_fragment']); }