Completed
Push — master ( f25568...a27cca )
by MusikAnimal
07:45 queued 01:25
created

AutomatedEditsHelper::isAutomated()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file contains only the AutomatedEditsHelper class.
4
 */
5
6
namespace AppBundle\Helper;
7
8
use Symfony\Component\DependencyInjection\ContainerInterface;
9
10
/**
11
 * Helper class for fetching semi-automated definitions.
12
 */
13
class AutomatedEditsHelper extends HelperBase
14
{
15
16
    /** @var string[] The list of tools that are considered reverting. */
17
    protected $revertTools = [];
18
19
    /** @var string[] The list of tool names and their regexes. */
20
    protected $tools = [];
21
22
    /**
23
     * AutomatedEditsHelper constructor.
24
     * @param ContainerInterface $container
25
     */
26 12
    public function __construct(ContainerInterface $container)
27
    {
28 12
        $this->container = $container;
0 ignored issues
show
Documentation Bug introduced by
$container is of type Symfony\Component\Depend...tion\ContainerInterface, but the property $container was declared to be of type Symfony\Component\DependencyInjection\Container. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof 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 given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
29 12
    }
30
31
    /**
32
     * Get the tool that matched the given edit summary.
33
     * This only works for tools defined with regular expressions, not tags.
34
     * @param  string $summary Edit summary
35
     * @param  string $projectDomain Such as en.wikipedia.org
36
     * @return string|bool Tool entry including key for 'name', or false if nothing was found
37
     */
38 7
    public function getTool($summary, $projectDomain)
39
    {
40 7
        foreach ($this->getTools($projectDomain) as $tool => $values) {
41 7
            if (isset($values['regex']) && preg_match('/'.$values['regex'].'/', $summary)) {
42 7
                return array_merge([
43 7
                    'name' => $tool,
44 7
                ], $values);
0 ignored issues
show
Bug introduced by
$values of type string is incompatible with the type null|array expected by parameter $array2 of array_merge(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

44
                ], /** @scrutinizer ignore-type */ $values);
Loading history...
45
            }
46
        }
47
48 5
        return false;
49
    }
50
51
    /**
52
     * Was the edit (semi-)automated, based on the edit summary?
53
     * This only works for tools defined with regular expressions, not tags.
54
     * @param  string $summary Edit summary
55
     * @param  string $projectDomain Such as en.wikipedia.org
56
     * @return bool
57
     */
58 1
    public function isAutomated($summary, $projectDomain)
59
    {
60 1
        return (bool) $this->getTool($summary, $projectDomain);
61
    }
62
63
    /**
64
     * Get list of automated tools and their associated info for the
65
     *   given project. This defaults to the 'default_project' if
66
     *   entries for the given project are not found.
67
     * @param  string $projectDomain Such as en.wikipedia.org
68
     * @return string[] Each tool with the tool name as the key,
69
     *   and 'link', 'regex' and/or 'tag' as the subarray keys.
70
     */
71 12
    public function getTools($projectDomain)
72
    {
73 12
        if (isset($this->tools[$projectDomain])) {
74 5
            return $this->tools[$projectDomain];
75
        }
76
77
        // Load the semi-automated edit types.
78 12
        $toolsByWiki = $this->container->getParameter('automated_tools');
79
80
        // Default to default project (e.g. en.wikipedia.org) if wiki not configured
81 12
        if (isset($toolsByWiki[$projectDomain])) {
82 9
            $this->tools[$projectDomain] = $toolsByWiki[$projectDomain];
83 3
        } elseif (isset($toolsByWiki[$this->container->getParameter('default_project')])) {
84 3
            $this->tools[$projectDomain] = $toolsByWiki[$this->container->getParameter('default_project')];
85
        } else {
86
            $this->tools[$projectDomain] = [];
87
        }
88
89
        // Override global rules with wiki-specific rules.
90 12
        $this->tools[$projectDomain] = $this->mergeValues(
91 12
            $this->tools[$projectDomain],
92 12
            $toolsByWiki['global']
93
        );
94
95 12
        return $this->tools[$projectDomain];
96
    }
97
98
    /**
99
     * Merges the given rule sets, giving priority to the wiki-specific set.
100
     * Regex is concatenated, not overridden.
101
     * @param array $localValues  The rule set for the local wiki.
102
     * @param array $globalValues The global rule set.
103
     */
104 12
    private function mergeValues($localValues, $globalValues)
105
    {
106
        // Initial set, including just the global values.
107 12
        $tools = $globalValues;
108
109
        // Loop through local values and override/merge as necessary.
110 12
        foreach ($localValues as $tool => $values) {
111 12
            $newValues = $values;
112
113 12
            if (isset($globalValues[$tool])) {
114
                // Order within array_merge is important, so that local values get priority.
115 12
                $newValues = array_merge($globalValues[$tool], $values);
116
            }
117
118
            // Regex should be merged, not overridden.
119 12
            if (isset($values['regex']) && isset($globalValues[$tool]['regex'])) {
120 1
                $newValues['regex'] = implode('|', [
121 1
                    $values['regex'],
122 1
                    $globalValues[$tool]['regex']
123
                ]);
124
            }
125
126 12
            $tools[$tool] = $newValues;
127
        }
128
129 12
        return $tools;
130
    }
131
132
    /**
133
     * Get only tools that are used to revert edits.
134
     * Revert detection happens only by testing against a regular expression,
135
     *   and not by checking tags.
136
     * @param  string $projectDomain Such as en.wikipedia.org
137
     * @return string[] Each tool with the tool name as the key,
138
     *   and 'link' and 'regex' as the subarray keys.
139
     */
140 5
    public function getRevertTools($projectDomain)
141
    {
142 5
        if (isset($this->revertTools[$projectDomain])) {
143 5
            return $this->revertTools[$projectDomain];
144
        }
145
146 5
        $revertEntries = array_filter(
147 5
            $this->getTools($projectDomain),
148
            function ($tool) {
149 5
                return isset($tool['revert']);
150 5
            }
151
        );
152
153
        // If 'revert' is set to `true`, the use 'regex' as the regular expression,
154
        //  otherwise 'revert' is assumed to be the regex string.
155 5
        $this->revertTools[$projectDomain] = array_map(function ($revertTool) {
156
            return [
157 5
                'link' => $revertTool['link'],
158 5
                'regex' => $revertTool['revert'] === true ? $revertTool['regex'] : $revertTool['revert']
159
            ];
160 5
        }, $revertEntries);
161
162 5
        return $this->revertTools[$projectDomain];
163
    }
164
165
    /**
166
     * Was the edit a revert, based on the edit summary?
167
     * This only works for tools defined with regular expressions, not tags.
168
     * @param  string $summary Edit summary
169
     * @param  string $projectDomain Such as en.wikipedia.org
170
     * @return bool
171
     */
172 5
    public function isRevert($summary, $projectDomain)
173
    {
174 5
        foreach ($this->getRevertTools($projectDomain) as $tool => $values) {
175 5
            if (preg_match('/'.$values['regex'].'/', $summary)) {
176 5
                return true;
177
            }
178
        }
179
180 5
        return false;
181
    }
182
}
183