Completed
Push — master ( 7aa1ec...cdd34c )
by Jakub
06:17
created

Generator.php$0 ➔ generate()   B

Complexity

Conditions 9

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 9

Importance

Changes 0
Metric Value
cc 9
c 0
b 0
f 0
dl 0
loc 40
ccs 29
cts 29
cp 1
crap 9
rs 7.7244
1
<?php
2
declare(strict_types=1);
3
4
namespace Nexendrie\Rss;
5
6
use Nexendrie\Rss\Extensions\RssCore;
7
use Symfony\Component\OptionsResolver\OptionsResolver;
8
use Nette\Utils\Arrays;
9
10
/**
11
 * RSS Channel Generator
12
 *
13
 * @author Jakub Konečný
14
 * @property-write callable $dataSource
15
 * @property string $template
16
 * @method void onBeforeGenerate(Generator $generator, array $info)
17
 * @method void onAddItem(Generator $generator, \SimpleXMLElement $channel, RssChannelItem $itemDefinition, \SimpleXMLElement $item)
0 ignored issues
show
introduced by
Line exceeds 120 characters; contains 132 characters
Loading history...
18
 * @method void onAfterGenerate(Generator $generator, array $info)
19
 */
20 1
final class Generator {
21
  use \Nette\SmartObject;
22
23
  private const NAMESPACE_ATTRIBUTE_HACK = "__extension_namespace__";
24
25
  public string $dateTimeFormat = "r";
26
  /** @var callable|null */
27
  protected $dataSource = null;
28
  public int $shortenDescription = 150;
29
  public string $generator = "Nexendrie RSS";
30
  public string $docs = "http://www.rssboard.org/rss-specification";
31
  protected string $template = __DIR__ . "/template.xml";
32
  /** @var RssExtensionsCollection|IRssExtension[] */
33
  public RssExtensionsCollection $extensions;
34
  /** @var callable[] */
35
  public array $onBeforeGenerate = [];
36
  /** @var callable[] */
37
  public array $onAddItem = [];
38
  /** @var callable[] */
39
  public array $onAfterGenerate = [];
40
41
  public function __construct() {
42 1
    $this->extensions = RssExtensionsCollection::fromArray([new RssCore()]);
43 1
  }
44
45
  protected function setDataSource(callable $dataSource): void {
46 1
    $this->dataSource = $dataSource;
47 1
  }
48
49
  protected function getTemplate(): string {
50 1
    return $this->template;
51
  }
52
  
53
  /**
54
   * @throws \RuntimeException
55
   */
56
  protected function setTemplate(string $template): void {
57 1
    if(!is_file($template) || !is_readable($template)) {
58 1
      throw new \RuntimeException("File $template does not exist or is not readable.");
59
    }
60 1
    $this->template = $template;
61 1
  }
62
  
63
  /**
64
   * @throws InvalidStateException
65
   * @throws \InvalidArgumentException
66
   */
67
  protected function getData(): Collection {
68 1
    if($this->dataSource === null) {
69 1
      throw new InvalidStateException("Data source for RSS generator is not set.");
70
    }
71 1
    $items = call_user_func($this->dataSource);
72 1
    if(!$items instanceof Collection) {
73 1
      throw new \InvalidArgumentException("Callback for data source for RSS generator has to return an instance of  " . Collection::class . ".");
0 ignored issues
show
introduced by
Line exceeds 120 characters; contains 145 characters
Loading history...
74
    }
75 1
    return $items;
76
  }
77
  
78
  protected function writeProperty(\SimpleXMLElement &$channel, array $info, string $property): void {
79 1
    $value = Arrays::get($info, $property, "");
80 1
    if(!$value instanceof IXmlConvertible) {
81 1
      $value = new GenericElement($property, $value);
82
    }
83 1
    $value->appendToXml($channel->channel);
84 1
  }
85
  
86
  /**
87
   * @throws InvalidStateException
88
   * @throws \InvalidArgumentException
89
   */
90
  public function generate(array $info): string {
91 1
    $this->onBeforeGenerate($this, $info);
92 1
    $items = $this->getData();
93 1
    $resolver = new OptionsResolver();
94 1
    foreach($this->extensions as $extension) {
95 1
      $extension->configureChannelOptions($resolver, $this);
96
    }
97 1
    $info = $resolver->resolve($info);
98
    /** @var \SimpleXMLElement $channel */
99 1
    $channel = simplexml_load_file($this->template);
100 1
    foreach($this->extensions as $extension) {
101 1
      if($extension->getName() !== "" && $extension->getNamespace() !== "") {
102 1
        $channel->addAttribute(static::NAMESPACE_ATTRIBUTE_HACK . $extension->getName(), $extension->getNamespace());
103
      }
104
    }
105 1
    $properties = $resolver->getDefinedOptions();
106 1
    foreach($properties as $property) {
107 1
      $this->writeProperty($channel, $info, $property);
108
    }
109 1
    if($this->generator !== "") {
110 1
      $channel->channel->generator = $this->generator;
111
    }
112 1
    if($this->docs !== "") {
113 1
      $channel->channel->docs = $this->docs;
114
    }
115
    /** @var RssChannelItem $item */
116 1
    foreach($items as $item) {
117
      /** @var \SimpleXMLElement $i */
118 1
      $i = $channel->channel->addChild("item");
119 1
      $item->toXml($i, $this);
120 1
      $this->onAddItem($this, $channel, $item, $i);
121
    }
122 1
    $this->onAfterGenerate($this, $info);
123 1
    $dom = new \DOMDocument();
124 1
    $dom->preserveWhiteSpace = false;
125 1
    $dom->formatOutput = true;
126 1
    $xml = $channel->asXML();
127 1
    $xml = str_replace(static::NAMESPACE_ATTRIBUTE_HACK, "xmlns:", $xml);
128 1
    $dom->loadXML($xml);
129 1
    return (string) $dom->saveXML();
130
  }
131
  
132
  /**
133
   * @throws InvalidStateException
134
   * @throws \InvalidArgumentException
135
   */
136
  public function response(array $info): RssResponse {
137 1
    return new RssResponse($this->generate($info));
138
  }
139
}
140
?>