Completed
Pull Request — master (#96)
by MusikAnimal
02:18
created

EditCounterController::logCountsApiAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 1
eloc 5
nc 1
nop 3
1
<?php
2
/**
3
 * This file contains only the EditCounterController class.
4
 */
5
6
namespace AppBundle\Controller;
7
8
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
9
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
10
use Symfony\Component\HttpFoundation\Request;
11
use Symfony\Component\HttpFoundation\Response;
12
use Symfony\Component\HttpFoundation\RedirectResponse;
13
use Symfony\Component\HttpFoundation\JsonResponse;
14
use Xtools\EditCounter;
15
use Xtools\EditCounterRepository;
16
use Xtools\Page;
17
use Xtools\Project;
18
use Xtools\ProjectRepository;
19
use Xtools\User;
20
use Xtools\UserRepository;
21
22
/**
23
 * Class EditCounterController
24
 */
25
class EditCounterController extends Controller
26
{
27
28
    /** @var User The user being queried. */
29
    protected $user;
30
31
    /** @var Project The project being queried. */
32
    protected $project;
33
34
    /** @var EditCounter The edit-counter, that does all the work. */
35
    protected $editCounter;
36
37
    /**
38
     * Get the tool's shortname.
39
     * @return string
40
     */
41
    public function getToolShortname()
42
    {
43
        return 'ec';
44
    }
45
46
    /**
47
     * Every action in this controller (other than 'index') calls this first.
48
     * @param string|bool $project The project name.
49
     * @param string|bool $username The username.
50
     */
51
    protected function setUpEditCounter($project = false, $username = false)
52
    {
53
        $this->project = ProjectRepository::getProject($project, $this->container);
0 ignored issues
show
Bug introduced by
It seems like $project defined by parameter $project on line 51 can also be of type boolean; however, Xtools\ProjectRepository::getProject() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
54
        $this->user = UserRepository::getUser($username, $this->container);
1 ignored issue
show
Bug introduced by
It seems like $username defined by parameter $username on line 51 can also be of type boolean; however, Xtools\UserRepository::getUser() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Compatibility introduced by
$this->container of type object<Symfony\Component...ion\ContainerInterface> is not a sub-type of object<Symfony\Component...ncyInjection\Container>. It seems like you assume a concrete implementation of the interface Symfony\Component\Depend...tion\ContainerInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
55
56
        // Don't continue if the user doesn't exist.
57
        if (!$this->user->existsOnProject($this->project)) {
58
            $this->addFlash('notice', 'user-not-found');
59
            return $this->redirectToRoute('ec');
60
        }
61
62
        // Get an edit-counter if we don't already have it set.
63
        if ($this->editCounter instanceof EditCounter) {
64
            return;
65
        }
66
        $editCounterRepo = new EditCounterRepository();
67
        $editCounterRepo->setContainer($this->container);
1 ignored issue
show
Compatibility introduced by
$this->container of type object<Symfony\Component...ion\ContainerInterface> is not a sub-type of object<Symfony\Component...ncyInjection\Container>. It seems like you assume a concrete implementation of the interface Symfony\Component\Depend...tion\ContainerInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
68
        $this->editCounter = new EditCounter($this->project, $this->user);
69
        $this->editCounter->setRepository($editCounterRepo);
70
    }
71
72
    /**
73
     * The initial GET request that displays the search form.
74
     *
75
     * @Route("/ec", name="ec")
76
     * @Route("/ec", name="EditCounter")
77
     * @Route("/ec/", name="EditCounterSlash")
78
     * @Route("/ec/index.php", name="EditCounterIndexPhp")
79
     * @Route("/ec/{project}", name="EditCounterProject")
80
     *
81
     * @param Request $request
82
     * @param string|null $project
83
     * @return RedirectResponse|Response
84
     */
85
    public function indexAction(Request $request, $project = null)
86
    {
87
        $queryProject = $request->query->get('project');
88
        $username = $request->query->get('username', $request->query->get('user'));
89
90
        if (($project || $queryProject) && $username) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $project of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
91
            $routeParams = [ 'project'=>($project ?: $queryProject), 'username' => $username ];
92
            return $this->redirectToRoute("EditCounterResult", $routeParams);
93
        } elseif (!$project && $queryProject) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $project of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
94
            return $this->redirectToRoute("EditCounterProject", [ 'project'=>$queryProject ]);
95
        }
96
97
        $project = ProjectRepository::getProject($queryProject, $this->container);
98
        if (!$project->exists()) {
99
            $project = ProjectRepository::getDefaultProject($this->container);
100
        }
101
102
        // Otherwise fall through.
103
        return $this->render('editCounter/index.html.twig', [
104
            'xtPageTitle' => 'tool-ec',
105
            'xtSubtitle' => 'tool-ec-desc',
106
            'xtPage' => 'ec',
107
            'project' => $project,
108
        ]);
109
    }
110
111
    /**
112
     * Display all results.
113
     * @Route("/ec/{project}/{username}", name="EditCounterResult")
114
     * @param Request $request
115
     * @param string $project
116
     * @param string $username
117
     * @return Response
118
     */
119
    public function resultAction(Request $request, $project, $username)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
120
    {
121
        $this->setUpEditCounter($project, $username);
122
123
        // Asynchronously collect some of the data that will be shown.
124
        // If multithreading is turned off, the normal getters in the views will
125
        // collect the necessary data synchronously.
126
        if ($this->container->getParameter('app.multithread.enable')) {
127
            $this->editCounter->prepareData($this->container);
0 ignored issues
show
Unused Code introduced by
The call to EditCounter::prepareData() has too many arguments starting with $this->container.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
128
        }
129
130
        // FIXME: is this needed? It shouldn't ever be a subrequest here in the resultAction.
131
        $isSubRequest = $this->container->get('request_stack')->getParentRequest() !== null;
132
133
        return $this->render('editCounter/result.html.twig', [
134
            'xtTitle' => $this->user->getUsername() . ' - ' . $this->project->getTitle(),
135
            'xtPage' => 'ec',
136
            'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..'),
137
            'is_sub_request' => $isSubRequest,
138
            'user' => $this->user,
139
            'project' => $this->project,
140
            'ec' => $this->editCounter,
141
        ]);
142
    }
143
144
    /**
145
     * Display the general statistics section.
146
     * @Route("/ec-generalstats/{project}/{username}", name="EditCounterGeneralStats")
147
     * @param string $project
148
     * @param string $username
149
     * @return Response
150
     */
151 View Code Duplication
    public function generalStatsAction($project, $username)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
152
    {
153
        $this->setUpEditCounter($project, $username);
154
155
        $isSubRequest = $this->get('request_stack')->getParentRequest() !== null;
156
        return $this->render('editCounter/general_stats.html.twig', [
157
            'xtTitle' => $this->user->getUsername(),
158
            'xtPage' => 'ec',
159
            'is_sub_request' => $isSubRequest,
160
            'user' => $this->user,
161
            'project' => $this->project,
162
            'ec' => $this->editCounter,
163
        ]);
164
    }
165
166
    /**
167
     * Display the namespace totals section.
168
     * @Route("/ec-namespacetotals/{project}/{username}", name="EditCounterNamespaceTotals")
169
     * @param Request $request
170
     * @param string $project
171
     * @param string $username
172
     * @return Response
173
     */
174 View Code Duplication
    public function namespaceTotalsAction(Request $request, $project, $username)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
175
    {
176
        $this->setUpEditCounter($project, $username);
177
        $isSubRequest = $this->get('request_stack')->getParentRequest() !== null;
178
        return $this->render('editCounter/namespace_totals.html.twig', [
179
            'xtTitle' => $this->user->getUsername(),
180
            'xtPage' => 'ec',
181
            'is_sub_request' => $isSubRequest,
182
            'user' => $this->user,
183
            'project' => $this->project,
184
            'ec' => $this->editCounter,
185
        ]);
186
    }
187
188
    /**
189
     * Display the timecard section.
190
     * @Route("/ec-timecard/{project}/{username}", name="EditCounterTimecard")
191
     * @param string $project
192
     * @param string $username
193
     * @return Response
194
     */
195 View Code Duplication
    public function timecardAction($project, $username)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
196
    {
197
        $this->setUpEditCounter($project, $username);
198
        $isSubRequest = $this->get('request_stack')->getParentRequest() !== null;
199
        $optedInPage = $this->project
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getPage() does only exist in the following sub-classes of Xtools\Repository: Xtools\ProjectRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
200
            ->getRepository()
201
            ->getPage($this->project, $this->project->userOptInPage($this->user));
202
        return $this->render('editCounter/timecard.html.twig', [
203
            'xtTitle' => $this->user->getUsername(),
204
            'xtPage' => 'ec',
205
            'is_sub_request' => $isSubRequest,
206
            'user' => $this->user,
207
            'project' => $this->project,
208
            'ec' => $this->editCounter,
209
            'opted_in_page' => $optedInPage,
210
        ]);
211
    }
212
213
    /**
214
     * Display the year counts section.
215
     * @Route("/ec-yearcounts/{project}/{username}", name="EditCounterYearCounts")
216
     * @param string $project
217
     * @param string $username
218
     * @return Response
219
     */
220 View Code Duplication
    public function yearcountsAction($project, $username)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
221
    {
222
        $this->setUpEditCounter($project, $username);
223
        $isSubRequest = $this->container->get('request_stack')->getParentRequest() !== null;
224
        return $this->render('editCounter/yearcounts.html.twig', [
225
            'xtTitle' => $this->user->getUsername(),
226
            'xtPage' => 'ec',
227
            'is_sub_request' => $isSubRequest,
228
            'user' => $this->user,
229
            'project' => $this->project,
230
            'ec' => $this->editCounter,
231
        ]);
232
    }
233
234
    /**
235
     * Display the month counts section.
236
     * @Route("/ec-monthcounts/{project}/{username}", name="EditCounterMonthCounts")
237
     * @param Request $request
238
     * @param string $project
239
     * @param string $username
240
     * @return Response
241
     */
242 View Code Duplication
    public function monthcountsAction(Request $request, $project, $username)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
243
    {
244
        $this->setUpEditCounter($project, $username);
245
        $isSubRequest = $this->container->get('request_stack')->getParentRequest() !== null;
246
        $optedInPage = $this->project
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getPage() does only exist in the following sub-classes of Xtools\Repository: Xtools\ProjectRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
247
            ->getRepository()
248
            ->getPage($this->project, $this->project->userOptInPage($this->user));
249
        return $this->render('editCounter/monthcounts.html.twig', [
250
            'xtTitle' => $this->user->getUsername(),
251
            'xtPage' => 'ec',
252
            'is_sub_request' => $isSubRequest,
253
            'user' => $this->user,
254
            'project' => $this->project,
255
            'ec' => $this->editCounter,
256
            'opted_in_page' => $optedInPage,
257
        ]);
258
    }
259
260
    /**
261
     * Display the latest global edits section.
262
     * @Route("/ec-latestglobal/{project}/{username}", name="EditCounterLatestGlobal")
263
     * @param Request $request The HTTP request.
264
     * @param string $project The project name.
265
     * @param string $username The username.
266
     * @return Response
267
     */
268 View Code Duplication
    public function latestglobalAction(Request $request, $project, $username)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
269
    {
270
        $this->setUpEditCounter($project, $username);
271
        $isSubRequest = $request->get('htmlonly')
272
                        || $this->container->get('request_stack')->getParentRequest() !== null;
273
        return $this->render('editCounter/latest_global.html.twig', [
274
            'xtTitle' => $this->user->getUsername(),
275
            'xtPage' => 'ec',
276
            'is_sub_request' => $isSubRequest,
277
            'user' => $this->user,
278
            'project' => $this->project,
279
            'ec' => $this->editCounter,
280
        ]);
281
    }
282
283
284
    /**
285
     * Below are internal API endpoints for the Edit Counter.
286
     * All only respond with JSON and only to requests from the localhost
287
     * (see access_control in security.yml).
288
     */
289
290
    /**
291
     * Get (most) of the general statistics as JSON.
292
     * @Route("/api/ec/pairdata/{project}/{username}", name="EditCounterApiPairData")
293
     * @param Request $request
294
     * @param string $project
295
     * @param string $username
296
     * @return JsonResponse
297
     */
298
    public function pairDataApiAction(Request $request, $project, $username)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
299
    {
300
        $this->setUpEditCounter($project, $username);
301
        return new JsonResponse(
302
            $this->editCounter->getPairData(),
303
            Response::HTTP_OK
304
        );
305
    }
306
307
    /**
308
     * Get various log counts for the user as JSON.
309
     * @Route("/api/ec/logcounts/{project}/{username}", name="EditCounterApiLogCounts")
310
     * @param Request $request
311
     * @param string $project
312
     * @param string $username
313
     * @return JsonResponse
314
     */
315
    public function logCountsApiAction(Request $request, $project, $username)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
316
    {
317
        $this->setUpEditCounter($project, $username);
318
        return new JsonResponse(
319
            $this->editCounter->getLogCounts(),
320
            Response::HTTP_OK
321
        );
322
    }
323
324
    /**
325
     * Get edit sizes for the user as JSON.
326
     * @Route("/api/ec/editsizes/{project}/{username}", name="EditCounterApiEditSizes")
327
     * @param Request $request
328
     * @param string $project
329
     * @param string $username
330
     * @return JsonResponse
331
     */
332
    public function editSizesApiAction(Request $request, $project, $username)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
333
    {
334
        $this->setUpEditCounter($project, $username);
335
        return new JsonResponse(
336
            $this->editCounter->getEditSizeData(),
337
            Response::HTTP_OK
338
        );
339
    }
340
341
    /**
342
     * Get the namespace totals for the user as JSON.
343
     * @Route("/api/ec/namespacetotals/{project}/{username}", name="EditCounterApiNamespaceTotals")
344
     * @param Request $request
345
     * @param string $project
346
     * @param string $username
347
     * @return Response
348
     */
349
    public function namespaceTotalsApiAction(Request $request, $project, $username)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
350
    {
351
        $this->setUpEditCounter($project, $username);
352
        return new JsonResponse(
353
            $this->editCounter->namespaceTotals(),
354
            Response::HTTP_OK
355
        );
356
    }
357
358
    /**
359
     * Display or fetch the month counts for the user.
360
     * @Route("/api/ec/monthcounts/{project}/{username}", name="EditCounterApiMonthCounts")
361
     * @param Request $request
362
     * @param string $project
363
     * @param string $username
364
     * @return Response
365
     */
366
    public function monthcountsApiAction(Request $request, $project, $username)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
367
    {
368
        $this->setUpEditCounter($project, $username);
369
        return new JsonResponse(
370
            $this->editCounter->monthCounts(),
371
            Response::HTTP_OK
372
        );
373
    }
374
375
376
    /**
377
     * Get global (system) edit counts for this user as JSON.
378
     * @Route("/api/ec/globaleditcounts/{project}/{username}", name="EditCounterApiGlobalEditCounts")
379
     * @param Request $request
380
     * @param string $project
381
     * @param string $username
382
     * @return JsonResponse
383
     */
384
    public function globalEditCountsApiAction(Request $request, $project, $username)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
385
    {
386
        $this->setUpEditCounter($project, $username);
387
        return new JsonResponse(
388
            $this->editCounter->getGlobalEditCounts(),
0 ignored issues
show
Bug introduced by
The method getGlobalEditCounts() does not exist on Xtools\EditCounter. Did you maybe mean globalEditCount()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
389
            Response::HTTP_OK
390
        );
391
    }
392
}
393