Browse Source

Add people to JSON Feed output

master
J. King 4 years ago
parent
commit
a81d84dedd
  1. 4
      lib/Collection.php
  2. 2
      lib/Feed.php
  3. 49
      lib/Parser/JSON/Construct.php
  4. 1
      lib/Parser/JSON/Entry.php
  5. 5
      lib/Parser/JSON/Feed.php
  6. 30
      lib/Parser/JSON/Primitives/Construct.php
  7. 22
      lib/Person/Person.php
  8. 33
      tests/cases/JSON/TestJSONFeed.php
  9. 120
      tests/cases/JSON/main.json

4
lib/Collection.php

@ -10,7 +10,7 @@ abstract class Collection implements \IteratorAggregate, \ArrayAccess, \Countabl
protected $data = [];
/** Implementation for IteratorAggreagate */
/** Implementation for IteratorAggregate */
public function getIterator(): \Traversable {
return ($this->data instanceof \Traversable) ? $this->data : new \ArrayIterator((array) $this->data);
}
@ -73,7 +73,7 @@ abstract class Collection implements \IteratorAggregate, \ArrayAccess, \Countabl
protected function filter(array $terms, string $axis, bool $inclusive): self {
$out = new static;
foreach ($this as $item) {
if (in_array($item->$axis, $terms)==$inclusive) {
if (in_array($item->$axis, $terms) == $inclusive) {
$out[] = $item;
}
}

2
lib/Feed.php

@ -21,7 +21,7 @@ class Feed {
*
* The format is largely advisory, but may be used when converting between formats
*/
public $type;
public $format;
/** @var string $version The format version of the news feed
*
* The version is largely advisory, but may be used when converting between formats

49
lib/Parser/JSON/Construct.php

@ -8,6 +8,8 @@ namespace JKingWeb\Lax\Parser\JSON;
use JKingWeb\Lax\Date;
use JKingWeb\Lax\Text;
use JKingWeb\Lax\Person\Collection as PersonCollection;
use JKingWeb\Lax\Person\Person;
trait Construct {
use \JKingWeb\Lax\Parser\Construct;
@ -30,7 +32,7 @@ trait Construct {
}
}
/** Returns an object member as a resolved URL */
/** Returns an object member as a resolved and normalized URL */
protected function fetchUrl(string $key, \stdClass $obj = null): ?string {
$url = $this->fetchMember($key, "str", $obj);
return (!is_null($url)) ? $this->resolveUrl($url, $this->url) : null;
@ -41,6 +43,7 @@ trait Construct {
return $this->parseDate($this->fetchMember($key, "str", $obj) ?? "");
}
/** Returns a plain-text string object member wrapped in a Text object */
protected function fetchText(string $key, \stdClass $obj = null): ?Text {
$t = $this->fetchMember($key, "str", $obj);
if (!is_null($t)) {
@ -48,4 +51,48 @@ trait Construct {
}
return null;
}
/** Retrieves the collection of authors as provided by version 1.1 of JSON Feed */
protected function getAuthorsV1(): ?PersonCollection {
$arr = $this->fetchMember("authors", "array");
if (!is_null($arr)) {
$out = new PersonCollection;
foreach ($arr as $o) {
if (is_object($o) && $p = $this->parseAuthor($o)) {
$out[] = $p;
}
}
return sizeof($out) ? $out : null;
}
return null;
}
/** Retrieves a collection containing a single author as provided by Version 1 of JSON Feed */
protected function getAuthorV1(): ?PersonCollection {
$o = $this->fetchMember("author", "object");
if ($o) {
$p = $this->parseAuthor($o);
if ($p) {
$out = new PersonCollection;
$out[] = $p;
return $out;
}
}
return null;
}
protected function parseAuthor(\stdClass $o): ?Person {
$p = new Person;
$p->name = $this->fetchMember("name", "str", $o);
$p->url = $this->fetchUrl("url", $o);
$p->avatar = $this->fetchUrl("avatar", $o);
if (array_filter((array) $p, function($v) {
return !is_null($v);
})) {
// if any keys are set the person is valid
$p->role = "author";
return $p;
}
return null;
}
}

1
lib/Parser/JSON/Entry.php

@ -13,7 +13,6 @@ use JKingWeb\Lax\Category\Category;
class Entry implements \JKingWeb\Lax\Parser\Entry {
use Construct;
use Primitives\Construct;
protected $url;

5
lib/Parser/JSON/Feed.php

@ -16,7 +16,6 @@ use JKingWeb\Lax\Date;
class Feed implements \JKingWeb\Lax\Parser\Feed {
use Construct;
use Primitives\Construct;
const MIME_TYPES = [
"application/json", // generic JSON
@ -71,8 +70,8 @@ class Feed implements \JKingWeb\Lax\Parser\Feed {
$feed->summary = $this->getSummary();
$feed->icon = $this->getIcon();
$feed->image = $this->getImage();
return $feed;
$feed->people = $this->getPeople();
return $feed;
$feed->dateModified = $this->getDateModified();
$feed->entries = $this->getEntries();
// do a second pass on missing data we'd rather fill in
@ -124,7 +123,7 @@ class Feed implements \JKingWeb\Lax\Parser\Feed {
/** General function to fetch a collection of people associated with a feed */
public function getPeople(): ?PersonCollection {
return $this->getPeopleV1();
return $this->getAuthorsV1() ?? $this->getAuthorV1();
}
/** General function to fetch the modification date of a feed

30
lib/Parser/JSON/Primitives/Construct.php

@ -1,30 +0,0 @@
<?php
/** @license MIT
* Copyright 2018 J. King et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Lax\Parser\JSON\Primitives;
use JKingWeb\Lax\Person\Person;
use JKingWeb\Lax\Person\Collection as PersonCollection;
use JKingWeb\Lax\Category\Collection as CategoryCollection;
trait Construct {
/** Primitive function to fetch the author from v1 JSON feeds */
protected function getPeopleV1() {
$author = $this->fetchMember("author", "object");
if (!isset($author)) {
return null;
} else {
$out = new PersonCollection;
$p = new Person;
$p->name = $this->fetchMember("name", "str", $author) ?? "";
$p->url = $this->fetchUrl("url", $author) ?? "";
$p->avatar = $this->fetchUrl("avatar", $author) ?? "";
$p->role = "author";
$out[] = $p;
return $out;
}
}
}

22
lib/Person/Person.php

@ -7,13 +7,23 @@ declare(strict_types=1);
namespace JKingWeb\Lax\Person;
class Person {
public $name = "";
public $mail = "";
public $url = "";
public $role = "";
public $avatar = "";
public $name = null;
public $mail = null;
public $url = null;
public $role = null;
public $avatar = null;
public function __toString() {
return strlen(strlen((string) $this->mail)) ? $this->name."<".$this->mail.">" : $this->name;
$name = strlen((string) $this->name) > 0;
$mail = strlen((string) $this->mail) > 0;
if ($name && $mail) {
return "{$this->name} <{$this->mail}>";
} elseif ($name) {
return $this->name;
} elseif ($mail) {
return $this->mail;
} else {
return "";
}
}
}

33
tests/cases/JSON/TestJSONFeed.php

@ -7,15 +7,18 @@ declare(strict_types=1);
namespace JKingWeb\Lax\TestCase\JSON;
use JKingWeb\Lax\Parser\Exception;
use JKingWeb\Lax\Feed;
use JKingWeb\Lax\Parser\JSON\Feed as Parser;
use JKingWeb\Lax\Feed;
use JKingWeb\Lax\Text;
use JKingWeb\Lax\Person\Person;
use JKingWeb\Lax\Person\Collection as PersonCollection;
/** @covers JKingWeb\Lax\Parser\JSON\Feed<extended> */
class TestJSON extends \PHPUnit\Framework\TestCase {
/** @dataProvider provideJSONFeedVersion1 */
public function testJSONFeedVersion1($input, string $type, $output): void {
if (is_array($input)) {
if (is_object($input)) {
$input = json_encode($input);
} elseif (!is_string($input)) {
throw new \Exception("Test input is invalid");
@ -33,24 +36,34 @@ class TestJSON extends \PHPUnit\Framework\TestCase {
public function provideJSONFeedVersion1(): iterable {
foreach (new \GlobIterator(__DIR__."/*.json", \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::KEY_AS_FILENAME) as $file => $path) {
foreach (json_decode(file_get_contents($path), true) as $index => $test) {
if (isset($test['exception'])) {
$test['output'] = new Exception((string) $test['exception']);
foreach (json_decode(file_get_contents($path)) as $index => $test) {
if (isset($test->exception)) {
$test->output = new Exception((string) $test->exception);
}
yield "$file #$index: {$test['description']}" => [
$test['input'],
$test['type'] ?? "",
$test['output'],
yield "$file #$index: {$test->description}" => [
$test->input,
$test->type ?? "",
$test->output,
];
}
}
}
protected function makeFeed(array $output): Feed {
protected function makeFeed(\stdClass $output): Feed {
$f = new Feed;
foreach ($output as $k => $v) {
if (in_array($k, ["title", "summary"])) {
$f->$k = $this->makeText($v);
} elseif ($k === "people") {
$c = new PersonCollection;
foreach ($v as $m) {
$p = new Person;
foreach ($m as $kk => $vv) {
$p->$kk = $vv;
}
$c[] = $p;
}
$f->$k = $c;
} else {
$f->$k = $v;
}

120
tests/cases/JSON/main.json

@ -57,6 +57,124 @@
"url": "http://example.com/"
}
},
{
"description": "Single author",
"input": {
"version": "https://jsonfeed.org/version/1",
"author": {"name": "John Doe", "url": "http://example.org/", "avatar": "http://example.org/avatar"}
},
"output": {
"format": "json",
"version": "1",
"people": [
{
"role": "author",
"name": "John Doe",
"url": "http://example.org/",
"avatar": "http://example.org/avatar"
}
]
}
},
{
"description": "Multiple authors",
"input": {
"version": "https://jsonfeed.org/version/1",
"authors": [
{"name": "John Doe", "url": "http://example.org/", "avatar": "http://example.org/avatar"},
{"name": "Jane Doe", "url": "http://example.net/", "avatar": "http://example.net/avatar"}
]
},
"output": {
"format": "json",
"version": "1",
"people": [
{
"role": "author",
"name": "John Doe",
"url": "http://example.org/",
"avatar": "http://example.org/avatar"
},
{
"role": "author",
"name": "Jane Doe",
"url": "http://example.net/",
"avatar": "http://example.net/avatar"
}
]
}
},
{
"description": "Fallback author",
"input": {
"version": "https://jsonfeed.org/version/1",
"authors": [
{"name": "John Doe", "url": "http://example.org/", "avatar": "http://example.org/avatar"},
{"name": "Jane Doe", "url": "http://example.net/", "avatar": "http://example.net/avatar"}
],
"author": {"name": "John Smith", "url": "http://example.biz/", "avatar": "http://example.biz/avatar"}
},
"output": {
"format": "json",
"version": "1",
"people": [
{
"role": "author",
"name": "John Doe",
"url": "http://example.org/",
"avatar": "http://example.org/avatar"
},
{
"role": "author",
"name": "Jane Doe",
"url": "http://example.net/",
"avatar": "http://example.net/avatar"
}
]
}
},
{
"description": "Empty author",
"input": {
"version": "https://jsonfeed.org/version/1",
"author": {}
},
"output": {
"format": "json",
"version": "1"
}
},
{
"description": "Empty authors",
"input": {
"version": "https://jsonfeed.org/version/1",
"authors": [{}]
},
"output": {
"format": "json",
"version": "1"
}
},
{
"description": "Empty authors with fallback",
"input": {
"version": "https://jsonfeed.org/version/1",
"authors": [{}],
"author": {"name": "John Doe", "url": "http://example.org/", "avatar": "http://example.org/avatar"}
},
"output": {
"format": "json",
"version": "1",
"people": [
{
"role": "author",
"name": "John Doe",
"url": "http://example.org/",
"avatar": "http://example.org/avatar"
}
]
}
},
{
"description": "Basic example",
"input": {
@ -79,7 +197,7 @@
"link": "http://example.net/",
"summary": "Example description",
"icon": "http://example.com/icon",
"image": "http://example.com/image",
"image": "http://example.com/image"
}
}
]
Loading…
Cancel
Save