Completed
Push — master ( 38e850...55d367 )
by Alex
02:05
created

StreamController::getModifiedSince()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 3
cts 4
cp 0.75
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 0
crap 2.0625
1
<?php
2
3
namespace Debril\RssAtomBundle\Controller;
4
5
use FeedIo\FeedInterface;
6
use Symfony\Component\HttpFoundation\Response;
7
use Symfony\Component\HttpFoundation\Request;
8
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
9
use Debril\RssAtomBundle\Provider\FeedContentProviderInterface;
10
use Debril\RssAtomBundle\Exception\FeedException\FeedNotFoundException;
11
12
/**
13
 * Class StreamController.
14
 */
15
class StreamController extends Controller
16
{
17
    /**
18
     * default provider.
19
     */
20
    const DEFAULT_SOURCE = 'debril.provider.default';
21
22
    /**
23
     * parameter used to force refresh at every hit (skips 'If-Modified-Since' usage).
24
     * set it to true for debug purpose.
25
     */
26
    const FORCE_PARAM_NAME = 'force_refresh';
27
28
    /**
29
     * @var \DateTime
30
     */
31
    protected $since;
32
33
    /**
34
     * @param Request $request
35
     *
36
     * @return Response
37
     */
38 4
    public function indexAction(Request $request)
39
    {
40 4
        $options = $request->attributes->get('_route_params');
41 4
        $this->setModifiedSince($request);
42 4
        $options['Since'] = $this->getModifiedSince();
43
44 4
        return $this->createStreamResponse(
45 4
            $options,
46 4
            $request->get('format', 'rss'),
47 4
            $request->get('source', self::DEFAULT_SOURCE)
48
        );
49
    }
50
51
    /**
52
     * Extract the 'If-Modified-Since' value from the headers.
53
     *
54
     * @return \DateTime
55
     */
56 4
    protected function getModifiedSince()
57
    {
58 4
        if (is_null($this->since)) {
59
            $this->since = new \DateTime('@0');
60
        }
61
62 4
        return $this->since;
63
    }
64
65
    /**
66
     * @param Request $request
67
     *
68
     * @return $this
69
     */
70 4
    protected function setModifiedSince(Request $request)
71
    {
72 4
        $this->since = new \DateTime();
73 4
        if ($request->headers->has('If-Modified-Since')) {
74 1
            $string = $request->headers->get('If-Modified-Since');
75 1
            $this->since = \DateTime::createFromFormat(\DateTime::RSS, $string);
0 ignored issues
show
Documentation Bug introduced by
It seems like \DateTime::createFromFor...DateTime::RSS, $string) can also be of type false. However, the property $since is declared as type object<DateTime>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
76
        } else {
77 4
            $this->since->setTimestamp(1);
78
        }
79
80 4
        return $this;
81
    }
82
83
    /**
84
     * Generate the HTTP response
85
     * 200 : a full body containing the stream
86
     * 304 : Not modified.
87
     *
88
     * @param array $options
89
     * @param $format
90
     * @param string $source
91
     *
92
     * @return Response
93
     *
94
     * @throws \Exception
95
     */
96 4
    protected function createStreamResponse(array $options, $format, $source = self::DEFAULT_SOURCE)
97
    {
98 4
        $content = $this->getContent($options, $source);
99
100 3
        if ($this->mustForceRefresh() || $content->getLastModified() > $this->getModifiedSince()) {
101 3
            $response = new Response($this->getFeedIo()->format($content, $format)->saveXML());
102 3
            $response->headers->set('Content-Type', 'application/xhtml+xml');
103 3
            $this->setFeedHeaders($response, $content);
104
105
        } else {
106 1
            $response = new Response();
107 1
            $response->setNotModified();
108
        }
109
110 3
        return $response;
111
    }
112
113
    /**
114
     * @param Response $response
115
     * @param FeedInterface $feed
116
     * @return $this
117
     */
118 3
    protected function setFeedHeaders(Response $response, FeedInterface $feed)
119
    {
120 3
        $response->headers->set('Content-Type', 'application/xhtml+xml');
121 3
        if (! $this->isPrivate() ) {
122 3
            $response->setPublic();
123
        }
124
125 3
        $response->setMaxAge(3600);
126 3
        $response->setLastModified($feed->getLastModified());
127
128 3
        return $this;
129
    }
130
131
    /**
132
     * Get the Stream's content using a FeedContentProviderInterface
133
     * The FeedContentProviderInterface instance is provided as a service
134
     * default : debril.provider.service.
135
     *
136
     * @param array  $options
137
     * @param string $source
138
     *
139
     * @return FeedInterface
140
     *
141
     * @throws \Exception
142
     */
143 4
    protected function getContent(array $options, $source)
144
    {
145 4
        $provider = $this->get($source);
146
147 4
        if (!$provider instanceof FeedContentProviderInterface) {
148
            throw new \Exception('Provider is not a FeedContentProviderInterface instance');
149
        }
150
151
        try {
152 4
            return $provider->getFeedContent($options);
153 1
        } catch (FeedNotFoundException $e) {
154 1
            throw $this->createNotFoundException('feed not found');
155
        }
156
    }
157
158
    /**
159
     * Returns true if the controller must ignore the last modified date.
160
     *
161
     * @return bool
162
     */
163 3
    protected function mustForceRefresh()
164
    {
165 3
        if ($this->container->hasParameter(self::FORCE_PARAM_NAME)) {
166
            return $this->container->getParameter(self::FORCE_PARAM_NAME);
167
        }
168
169 3
        return false;
170
    }
171
172
    /**
173
     * @return boolean true if the feed must be private
174
     */
175 3
    protected function isPrivate()
176
    {
177 3
        return $this->container->getParameter('debril_rss_atom.private_feeds');
178
    }
179
180
    /**
181
     * @return \FeedIo\FeedIo
182
     */
183 3
    protected function getFeedIo()
184
    {
185 3
        return $this->container->get('feedio');
186
    }
187
188
}
189