]> git.immae.eu Git - github/wallabag/wallabag.git/blob - src/Wallabag/ImportBundle/Import/AbstractImport.php
Avoid returning objects passed by reference.
[github/wallabag/wallabag.git] / src / Wallabag / ImportBundle / Import / AbstractImport.php
1 <?php
2
3 namespace Wallabag\ImportBundle\Import;
4
5 use Psr\Log\LoggerInterface;
6 use Psr\Log\NullLogger;
7 use Doctrine\ORM\EntityManager;
8 use Wallabag\CoreBundle\Helper\ContentProxy;
9 use Wallabag\CoreBundle\Entity\Entry;
10 use Wallabag\CoreBundle\Entity\Tag;
11 use Wallabag\CoreBundle\Helper\TagsAssigner;
12 use Wallabag\UserBundle\Entity\User;
13 use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface;
14 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
15 use Wallabag\CoreBundle\Event\EntrySavedEvent;
16
17 abstract class AbstractImport implements ImportInterface
18 {
19 protected $em;
20 protected $logger;
21 protected $contentProxy;
22 protected $tagsAssigner;
23 protected $eventDispatcher;
24 protected $producer;
25 protected $user;
26 protected $markAsRead;
27 protected $skippedEntries = 0;
28 protected $importedEntries = 0;
29 protected $queuedEntries = 0;
30
31 public function __construct(EntityManager $em, ContentProxy $contentProxy, TagsAssigner $tagsAssigner, EventDispatcherInterface $eventDispatcher)
32 {
33 $this->em = $em;
34 $this->logger = new NullLogger();
35 $this->contentProxy = $contentProxy;
36 $this->tagsAssigner = $tagsAssigner;
37 $this->eventDispatcher = $eventDispatcher;
38 }
39
40 public function setLogger(LoggerInterface $logger)
41 {
42 $this->logger = $logger;
43 }
44
45 /**
46 * Set RabbitMQ/Redis Producer to send each entry to a queue.
47 * This method should be called when user has enabled RabbitMQ.
48 *
49 * @param ProducerInterface $producer
50 */
51 public function setProducer(ProducerInterface $producer)
52 {
53 $this->producer = $producer;
54 }
55
56 /**
57 * Set current user.
58 * Could the current *connected* user or one retrieve by the consumer.
59 *
60 * @param User $user
61 */
62 public function setUser(User $user)
63 {
64 $this->user = $user;
65 }
66
67 /**
68 * Set whether articles must be all marked as read.
69 *
70 * @param bool $markAsRead
71 */
72 public function setMarkAsRead($markAsRead)
73 {
74 $this->markAsRead = $markAsRead;
75
76 return $this;
77 }
78
79 /**
80 * Get whether articles must be all marked as read.
81 */
82 public function getMarkAsRead()
83 {
84 return $this->markAsRead;
85 }
86
87 /**
88 * Fetch content from the ContentProxy (using graby).
89 * If it fails return the given entry to be saved in all case (to avoid user to loose the content).
90 *
91 * @param Entry $entry Entry to update
92 * @param string $url Url to grab content for
93 * @param array $content An array with AT LEAST keys title, html, url, language & content_type to skip the fetchContent from the url
94 */
95 protected function fetchContent(Entry $entry, $url, array $content = [])
96 {
97 try {
98 $this->contentProxy->updateEntry($entry, $url, $content);
99 } catch (\Exception $e) {
100 return $entry;
101 }
102 }
103
104 /**
105 * Parse and insert all given entries.
106 *
107 * @param $entries
108 */
109 protected function parseEntries($entries)
110 {
111 $i = 1;
112 $entryToBeFlushed = [];
113
114 foreach ($entries as $importedEntry) {
115 if ($this->markAsRead) {
116 $importedEntry = $this->setEntryAsRead($importedEntry);
117 }
118
119 $entry = $this->parseEntry($importedEntry);
120
121 if (null === $entry) {
122 continue;
123 }
124
125 // store each entry to be flushed so we can trigger the entry.saved event for each of them
126 // entry.saved needs the entry to be persisted in db because it needs it id to generate
127 // images (at least)
128 $entryToBeFlushed[] = $entry;
129
130 // flush every 20 entries
131 if (($i % 20) === 0) {
132 $this->em->flush();
133
134 foreach ($entryToBeFlushed as $entry) {
135 $this->eventDispatcher->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
136 }
137
138 $entryToBeFlushed = [];
139
140 // clear only affected entities
141 $this->em->clear(Entry::class);
142 $this->em->clear(Tag::class);
143 }
144 ++$i;
145 }
146
147 $this->em->flush();
148
149 if (!empty($entryToBeFlushed)) {
150 foreach ($entryToBeFlushed as $entry) {
151 $this->eventDispatcher->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
152 }
153 }
154 }
155
156 /**
157 * Parse entries and send them to the queue.
158 * It should just be a simple loop on all item, no call to the database should be done
159 * to speedup queuing.
160 *
161 * Faster parse entries for Producer.
162 * We don't care to make check at this time. They'll be done by the consumer.
163 *
164 * @param array $entries
165 */
166 protected function parseEntriesForProducer(array $entries)
167 {
168 foreach ($entries as $importedEntry) {
169 // set userId for the producer (it won't know which user is connected)
170 $importedEntry['userId'] = $this->user->getId();
171
172 if ($this->markAsRead) {
173 $importedEntry = $this->setEntryAsRead($importedEntry);
174 }
175
176 ++$this->queuedEntries;
177
178 $this->producer->publish(json_encode($importedEntry));
179 }
180 }
181
182 /**
183 * {@inheritdoc}
184 */
185 public function getSummary()
186 {
187 return [
188 'skipped' => $this->skippedEntries,
189 'imported' => $this->importedEntries,
190 'queued' => $this->queuedEntries,
191 ];
192 }
193
194 /**
195 * Parse one entry.
196 *
197 * @param array $importedEntry
198 *
199 * @return Entry
200 */
201 abstract public function parseEntry(array $importedEntry);
202
203 /**
204 * Set current imported entry to archived / read.
205 * Implementation is different accross all imports.
206 *
207 * @param array $importedEntry
208 *
209 * @return array
210 */
211 abstract protected function setEntryAsRead(array $importedEntry);
212 }