Completed
Push — master ( 19d49a...37b30d )
by Alex
08:28 queued 05:53
created

Parser::addAcceptableItem()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 3
crap 1
1
<?php
2
3
/**
4
 * Rss/Atom Bundle for Symfony.
5
 *
6
 *
7
 * @license http://opensource.org/licenses/lgpl-3.0.html LGPL
8
 * @copyright (c) 2013, Alexandre Debril
9
 */
10
namespace Debril\RssAtomBundle\Protocol;
11
12
use Debril\RssAtomBundle\Protocol\Filter\ModifiedSince;
13
use Debril\RssAtomBundle\Protocol\Parser\Item;
14
use Debril\RssAtomBundle\Exception\ParserException;
15
use Debril\RssAtomBundle\Protocol\Parser\Factory;
16
use Debril\RssAtomBundle\Protocol\Parser\Media;
17
18
/**
19
 * Class Parser.
20
 */
21
abstract class Parser
22
{
23
    /**
24
     * System's time zone.
25
     *
26
     * @var \DateTimeZone
27
     */
28
    protected static $timezone;
29
30
    /**
31
     * List of mandatory fields.
32
     *
33
     * @var string[]
34
     */
35
    protected $mandatoryFields = array();
36
37
    /**
38
     * Feed's date format.
39
     *
40
     * @var string[]
41
     */
42
    protected $dateFormats = array();
43
44
    /**
45
     * @var Factory
46
     */
47
    protected $factory;
48
49
    /**
50
     * Parses the feed's body to create a FeedContent instance.
51
     *
52
     * @param \SimpleXMLElement                            $xmlBody
53
     * @param \Debril\RssAtomBundle\Protocol\FeedInterface $feed
54
     * @param array                                        $filters
55
     *
56
     * @throws ParserException
57
     *
58
     * @return FeedInterface
59
     */
60 5
    public function parse(\SimpleXMLElement $xmlBody, FeedInterface $feed, array $filters = array())
61
    {
62 5
        if (!$this->canHandle($xmlBody)) {
63 1
            throw new ParserException('this is not a supported format');
64
        }
65
66 4
        $this->checkBodyStructure($xmlBody);
67
68 4
        $xmlBody = $this->registerNamespaces($xmlBody);
69
70 4
        return $this->parseBody($xmlBody, $feed, $filters);
71
    }
72
73
    /**
74
     * @param \SimpleXMLElement $body
75
     *
76
     * @throws ParserException
77
     */
78 5
    protected function checkBodyStructure(\SimpleXMLElement $body)
79
    {
80 5
        $errors = array();
81
82 5
        foreach ($this->mandatoryFields as $field) {
83 5
            if (!isset($body->$field)) {
84 2
                $errors[] = "missing {$field}";
85 2
            }
86 5
        }
87
88 5
        if (0 < count($errors)) {
89 2
            $report = implode(', ', $errors);
90 2
            throw new ParserException(
91 2
                "error while parsing the feed : {$report}"
92 2
            );
93
        }
94 3
    }
95
96
    /**
97
     * @param array $dates
98
     */
99 10
    public function setDateFormats(array $dates)
100
    {
101 10
        $this->dateFormats = $dates;
102 10
    }
103
104
    /**
105
     * @param string $date
106
     *
107
     * @return string date Format
108
     *
109
     * @throws ParserException
110
     */
111 9
    public function guessDateFormat($date)
112
    {
113 9
        foreach ($this->dateFormats as $format) {
114 9
            $test = \DateTime::createFromFormat($format, $date);
115 9
            if ($test instanceof \DateTime) {
116 6
                return $format;
117
            }
118 6
        }
119
120 3
        throw new ParserException('Impossible to guess date format : '.$date);
121
    }
122
123
    /**
124
     * @return ItemInInterface
125
     */
126 3
    public function newItem()
127
    {
128 3
        if ($this->getFactory() instanceof Factory) {
129 1
            return $this->getFactory()->newItem();
130
        }
131
132 2
        return new Item();
133
    }
134
135
    /**
136
     * @return Factory
137
     */
138 3
    public function getFactory()
139
    {
140 3
        return $this->factory;
141
    }
142
143
    /**
144
     * @param Factory $factory
145
     *
146
     * @return Parser
147
     */
148 2
    public function setFactory(Factory $factory)
149
    {
150 2
        $this->factory = $factory;
151
152 2
        return $this;
153
    }
154
155
    /**
156
     * @deprecated since 1.3.0 replaced by addValidItem
157
     *
158
     * @param FeedInInterface $feed
159
     * @param ItemInInterface $item
160
     * @param \DateTime       $modifiedSince
161
     *
162
     * @return $this
163
     */
164 2
    public function addAcceptableItem(FeedInInterface $feed, ItemInInterface $item, \DateTime $modifiedSince)
165
    {
166
        $filters = array(
167 2
            new ModifiedSince($modifiedSince),
168 2
        );
169
170 2
        return $this->addValidItem($feed, $item, $filters);
171
    }
172
173
    /**
174
     * @param FeedInInterface $feed
175
     * @param ItemInInterface $item
176
     * @param array           $filters
177
     *
178
     * @return $this
179
     */
180 3
    public function addValidItem(FeedInInterface $feed, ItemInInterface $item, array $filters = array())
181
    {
182 3
        if ($this->isValid($item, $filters)) {
183 3
            $feed->addItem($item);
184 3
        }
185
186 3
        return $this;
187
    }
188
189
    /**
190
     * @param ItemInInterface $item
191
     * @param array           $filters
192
     *
193
     * @return bool
194
     */
195 3
    public function isValid(ItemInInterface $item, array $filters = array())
196
    {
197 3
        $valid = true;
198 3
        foreach ($filters as $filter) {
199 3
            if ($filter instanceof FilterInterface) {
200 3
                $valid = $filter->isValid($item) ? $valid : false;
0 ignored issues
show
Documentation introduced by
$item is of type object<Debril\RssAtomBun...otocol\ItemInInterface>, but the function expects a object<Debril\RssAtomBun...tocol\ItemOutInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
201 3
            }
202 3
        }
203
204 3
        return $valid;
205
    }
206
207
    /**
208
     * Creates a DateTime instance for the given string. Default format is RFC2822.
209
     *
210
     * @param string $string
211
     * @param string $format
212
     *
213
     * @return \DateTime
214
     */
215 5
    public static function convertToDateTime($string, $format = \DateTime::RFC2822)
216
    {
217 5
        $date = \DateTime::createFromFormat($format, $string);
218
219 5
        if (!$date instanceof \DateTime) {
220 1
            throw new ParserException("date is the wrong format : {$string} - expected {$format}");
221
        }
222
223 4
        $date->setTimezone(static::getSystemTimezone());
224
225 4
        return $date;
226
    }
227
228
    /**
229
     * Returns the system's timezone.
230
     *
231
     * @return \DateTimeZone
232
     */
233 4
    public static function getSystemTimezone()
234
    {
235 4
        if (is_null(static::$timezone)) {
236 1
            static::$timezone = new \DateTimeZone(date_default_timezone_get());
237 1
        }
238
239 4
        return static::$timezone;
240
    }
241
242
    /**
243
     * Reset the system's time zone.
244
     */
245 1
    public static function resetTimezone()
246
    {
247 1
        static::$timezone = null;
248 1
    }
249
250
    /**
251
     * register Namespaces.
252
     *
253
     * @param \SimpleXMLElement $xmlBody
254
     *
255
     * @return \SimpleXMLElement
256
     */
257 3
    protected function registerNamespaces(\SimpleXMLElement $xmlBody)
258
    {
259 3
        $namespaces = $xmlBody->getNamespaces(true);
260 3
        foreach ($namespaces as $prefix => $ns) {
261 3
            if ($prefix != '') {
262 1
                $xmlBody->registerXPathNamespace($prefix, $ns);
263 1
            }
264 3
        }
265
266 3
        return $xmlBody;
267
    }
268
269
    /**
270
     * @param \SimpleXMLElement $xmlElement
271
     * @param array             $namespaces
272
     *
273
     * @return array
274
     */
275 3
    protected function getAdditionalNamespacesElements(\SimpleXMLElement $xmlElement, $namespaces)
276
    {
277 3
        $additional = array();
278 3
        foreach ($namespaces as $prefix => $ns) {
279 3
            if ($prefix != '') {
280 1
                $additionalElement = $xmlElement->children($ns);
281 1
                if (!empty($additionalElement)) {
282 1
                    $additional[$prefix] = $additionalElement;
283 1
                }
284 1
            }
285 3
        }
286
287 3
        return $additional;
288
    }
289
290
    /**
291
     * @param \SimpleXMLElement $element
292
     * @param string            $attributeName
293
     *
294
     * @return string|null
295
     */
296 6
    public function getAttributeValue(\SimpleXMLElement $element, $attributeName)
297
    {
298 6
        $attributes = $element[0]->attributes();
299 6
        foreach ($attributes as $name => $value) {
300 6
            if (strcasecmp($name, $attributeName) === 0) {
301 6
                return (string) $value;
302
            }
303 6
        }
304
305 5
        return;
306
    }
307
308
    /**
309
     * @param \SimpleXMLElement $element
310
     *
311
     * @return Media
312
     */
313 2
    public function createMedia(\SimpleXMLElement $element)
314
    {
315 2
        $media = new Media();
316 2
        $media->setUrl($this->searchAttributeValue($element, array('url', 'href', 'link')))
317 2
              ->setType($this->getAttributeValue($element, 'type'))
318 2
              ->setLength($this->getAttributeValue($element, 'length'));
319
320 2
        return $media;
321
    }
322
323
    /**
324
     * Looks for an attribute value under different possible names.
325
     *
326
     * @param \SimpleXMLElement $element
327
     * @param array             $names
328
     *
329
     * @return null|string|void
330
     */
331 3
    public function searchAttributeValue(\SimpleXMLElement $element, array $names)
332
    {
333 3
        foreach ($names as $name) {
334 3
            $value = $this->getAttributeValue($element, $name);
335 3
            if (!is_null($value)) {
336 3
                return (string) $value;
337
            }
338 3
        }
339
340 2
        return;
341
    }
342
343
    /**
344
     * Tells if the parser can handle the feed or not.
345
     *
346
     * @param \SimpleXMLElement $xmlBody
347
     *
348
     * @return bool
349
     */
350
    abstract public function canHandle(\SimpleXMLElement $xmlBody);
351
352
    /**
353
     * Performs the actual conversion into a FeedContent instance.
354
     *
355
     * @param \SimpleXMLElement $body
356
     * @param FeedInterface     $feed
357
     * @param array             $filters
358
     *
359
     * @return FeedInInterface
360
     */
361
    abstract protected function parseBody(\SimpleXMLElement $body, FeedInterface $feed, array $filters);
362
363
    /**
364
     * Handles enclosures if any.
365
     *
366
     * @param \SimpleXMLElement $element
367
     * @param ItemInInterface   $item
368
     *
369
     * @return $this
370
     */
371
    abstract protected function handleEnclosure(\SimpleXMLElement $element, ItemInInterface $item);
372
}
373