Passed
Push — master ( 8b9089...a51f69 )
by MusikAnimal
01:23
created

Edit::getYear()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * This file contains only the Edit class.
4
 */
5
6
namespace Xtools;
7
8
use Xtools\User;
9
use Symfony\Component\DependencyInjection\Container;
10
use DateTime;
11
12
/**
13
 * An Edit is a single edit to a page on one project.
14
 */
15
class Edit extends Model
16
{
17
18
    /** @var Page the page associated with this edit */
19
    protected $page;
20
21
    /** @var int ID of the revision */
22
    protected $id;
23
24
    /** @var DateTime Timestamp of the revision */
25
    protected $timestamp;
26
27
    /** @var bool Whether or not this edit was a minor edit */
28
    protected $minor;
29
30
    /** @var int|string|null Length of the page as of this edit, in bytes */
31
    protected $length;
32
33
    /** @var int|string|null The diff size of this edit */
34
    protected $lengthChange;
35
36
    /** @var User - User object of who made the edit */
37
    protected $user;
38
39
    /** @var string The edit summary */
40
    protected $comment;
41
42
    /**
43
     * Edit constructor.
44
     * @param Page $page
45
     * @param string[] $attrs Attributes, as retrieved by PagesRepository->getRevisions()
46
     */
47 12
    public function __construct(Page $page, $attrs)
48
    {
49 12
        $this->page = $page;
50
51
        // Copy over supported attributes
52 12
        $this->id = (int) $attrs['id'];
53
54
        // Allow DateTime or string (latter assumed to be of format YmdHis)
55 12
        if ($attrs['timestamp'] instanceof DateTime) {
56
            $this->timestamp = $attrs['timestamp'];
57
        } else {
58 12
            $this->timestamp = DateTime::createFromFormat('YmdHis', $attrs['timestamp']);
0 ignored issues
show
Documentation Bug introduced by
It seems like \DateTime::createFromFor...', $attrs['timestamp']) can also be of type false. However, the property $timestamp 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...
59
        }
60
61 12
        $this->minor = $attrs['minor'] === '1';
62
63
        // NOTE: Do not type cast into an integer. Null values are
64
        //   our indication that the revision was revision-deleted.
65 12
        $this->length = $attrs['length'];
66 12
        $this->lengthChange = $attrs['length_change'];
67
68 12
        $this->user = new User($attrs['username']);
69 12
        $this->comment = $attrs['comment'];
70 12
    }
71
72
    /**
73
     * Unique identifier for this Edit, to be used in cache keys.
74
     * @see Repository::getCacheKey()
75
     * @return string
76
     */
77
    public function getCacheKey()
78
    {
79
        return $this->id;
80
    }
81
82
    /**
83
     * Get the page to which this edit belongs.
84
     * @return Page
85
     */
86 10
    public function getPage()
87
    {
88 10
        return $this->page;
89
    }
90
91
    /**
92
     * ID of the edit.
93
     * @return int
94
     */
95 4
    public function getId()
96
    {
97 4
        return $this->id;
98
    }
99
100
    /**
101
     * Get the edit's timestamp.
102
     * @return DateTime
103
     */
104 4
    public function getTimestamp()
105
    {
106 4
        return $this->timestamp;
107
    }
108
109
    /**
110
     * Year the revision was made.
111
     * @return string
112
     */
113 4
    public function getYear()
114
    {
115 4
        return $this->timestamp->format('Y');
116
    }
117
118
    /**
119
     * Get the numeric representation of the month the revision was made, with leading zeros.
120
     * @return string
121
     */
122 4
    public function getMonth()
123
    {
124 4
        return $this->timestamp->format('m');
125
    }
126
127
    /**
128
     * Whether or not this edit was a minor edit.
129
     * @return bool
130
     */
131 4
    public function getMinor()
132
    {
133 4
        return $this->minor;
134
    }
135
136
    /**
137
     * Alias of getMinor()
138
     * @return bool Whether or not this edit was a minor edit
139
     */
140 4
    public function isMinor()
141
    {
142 4
        return $this->getMinor();
143
    }
144
145
    /**
146
     * Length of the page as of this edit, in bytes.
147
     * @see Edit::getSize() Edit::getSize() for the size <em>change</em>.
148
     * @return int
149
     */
150 4
    public function getLength()
151
    {
152 4
        return $this->length;
153
    }
154
155
    /**
156
     * The diff size of this edit.
157
     * @return int Signed length change in bytes.
158
     */
159 4
    public function getSize()
160
    {
161 4
        return $this->lengthChange;
162
    }
163
164
    /**
165
     * Alias of getSize()
166
     * @return int The diff size of this edit
167
     */
168 1
    public function getLengthChange()
169
    {
170 1
        return $this->getSize();
171
    }
172
173
    /**
174
     * Get the user who made the edit.
175
     * @return User
176
     */
177 5
    public function getUser()
178
    {
179 5
        return $this->user;
180
    }
181
182
    /**
183
     * Get the edit summary.
184
     * @return string
185
     */
186 1
    public function getComment()
187
    {
188 1
        return $this->comment;
189
    }
190
191
    /**
192
     * Get the edit summary (alias of Edit::getComment()).
193
     * @return string
194
     */
195 1
    public function getSummary()
196
    {
197 1
        return $this->getComment();
198
    }
199
200
    /**
201
     * Get edit summary as 'wikified' HTML markup
202
     * @param bool $useUnnormalizedPageTitle Use the unnormalized page title to avoid
203
     *   an API call. This should be used only if you fetched the page title via other
204
     *   means (SQL query), and is not from user input alone.
205
     * @return string Safe HTML
206
     */
207 1
    public function getWikifiedComment($useUnnormalizedPageTitle = false)
208
    {
209 1
        $summary = htmlspecialchars($this->getSummary(), ENT_NOQUOTES);
210 1
        $sectionMatch = null;
211 1
        $isSection = preg_match_all("/^\/\* (.*?) \*\//", $summary, $sectionMatch);
212
213 1
        if ($isSection) {
214
            $pageUrl = $this->getProject()->getUrl(false) . str_replace(
215
                '$1',
216
                $this->getPage()->getTitle($useUnnormalizedPageTitle),
217
                $this->getProject()->getArticlePath()
218
            );
219
            $sectionTitle = $sectionMatch[1][0];
220
221
            // Must have underscores for the link to properly go to the section
222
            $sectionTitleLink = htmlspecialchars(str_replace(' ', '_', $sectionTitle));
223
224
            $sectionWikitext = "<a target='_blank' href='$pageUrl#$sectionTitleLink'>&rarr;</a>" .
225
                "<em class='text-muted'>" . htmlspecialchars($sectionTitle) . ":</em> ";
226
            $summary = str_replace($sectionMatch[0][0], $sectionWikitext, $summary);
227
        }
228
229 1
        $linkMatch = null;
230
231 1
        while (preg_match_all("/\[\[:?(.*?)\]\]/", $summary, $linkMatch)) {
232 1
            $wikiLinkParts = explode('|', $linkMatch[1][0]);
233 1
            $wikiLinkPath = htmlspecialchars($wikiLinkParts[0]);
234 1
            $wikiLinkText = htmlspecialchars(
235 1
                isset($wikiLinkParts[1]) ? $wikiLinkParts[1] : $wikiLinkPath
236
            );
237
238
            // Use normalized page title (underscored, capitalized)
239 1
            $pageUrl = $this->getProject()->getUrl(false) . str_replace(
240 1
                '$1',
241 1
                ucfirst(str_replace(' ', '_', $wikiLinkPath)),
242 1
                $this->getProject()->getArticlePath()
243
            );
244
245 1
            $link = "<a target='_blank' href='$pageUrl'>$wikiLinkText</a>";
246 1
            $summary = str_replace($linkMatch[0][0], $link, $summary);
247
        }
248
249 1
        return $summary;
250
    }
251
252
    /**
253
     * Get edit summary as 'wikified' HTML markup (alias of Edit::getWikifiedSummary()).
254
     * @return string
255
     */
256 1
    public function getWikifiedSummary()
257
    {
258 1
        return $this->getWikifiedComment();
259
    }
260
261
    /**
262
     * Get the project this edit was made on
263
     * @return Project
264
     */
265 10
    public function getProject()
266
    {
267 10
        return $this->getPage()->getProject();
268
    }
269
270
    /**
271
     * Get the full URL to the diff of the edit
272
     * @return string
273
     */
274 1
    public function getDiffUrl()
275
    {
276 1
        $project = $this->getProject();
277 1
        $path = str_replace('$1', 'Special:Diff/' . $this->id, $project->getArticlePath());
278 1
        return rtrim($project->getUrl(), '/') . $path;
279
    }
280
281
    /**
282
     * Get the full permanent URL to the page at the time of the edit
283
     * @return string
284
     */
285 1
    public function getPermaUrl()
286
    {
287 1
        $project = $this->getProject();
288 1
        $path = str_replace('$1', 'Special:PermaLink/' . $this->id, $project->getArticlePath());
289 1
        return rtrim($project->getUrl(), '/') . $path;
290
    }
291
292
    /**
293
     * Was the edit a revert, based on the edit summary?
294
     * @param Container $container The DI container.
295
     * @return bool
296
     */
297 4
    public function isRevert(Container $container)
298
    {
299 4
        $automatedEditsHelper = $container->get('app.automated_edits_helper');
300 4
        return $automatedEditsHelper->isRevert($this->comment, $this->getProject()->getDomain());
301
    }
302
303
    /**
304
     * Get the name of the tool that was used to make this edit.
305
     * @param Container $container The DI container.
306
     * @return string|false The name of the tool that was used to make the edit
307
     */
308 5
    public function getTool(Container $container)
309
    {
310 5
        $automatedEditsHelper = $container->get('app.automated_edits_helper');
311 5
        return $automatedEditsHelper->getTool($this->comment, $this->getProject()->getDomain());
312
    }
313
314
    /**
315
     * Was the edit (semi-)automated, based on the edit summary?
316
     * @param  Container $container [description]
317
     * @return bool
318
     */
319 1
    public function isAutomated(Container $container)
320
    {
321 1
        return (bool) $this->getTool($container);
322
    }
323
324
    /**
325
     * Was the edit made by a logged out user?
326
     * @return bool
327
     */
328 4
    public function isAnon()
329
    {
330 4
        return $this->getUser()->isAnon();
331
    }
332
}
333