Completed
Pull Request — master (#49)
by
unknown
02:21
created

ContentReviewEmails::run()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 51
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 15
Bugs 1 Features 1
Metric Value
c 15
b 1
f 1
dl 0
loc 51
rs 8.6588
cc 5
eloc 23
nc 10
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Daily task to send emails to the owners of content items when the review date rolls around.
5
 */
6
class ContentReviewEmails extends BuildTask
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
7
{
8
    /**
9
     * @param SS_HTTPRequest $request
10
     */
11
    public function run($request)
12
    {
13
        $compatibility = ContentReviewCompatability::start();
0 ignored issues
show
Unused Code introduced by
$compatibility is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
14
15
        $now = SS_Datetime::now();
16
17
        // First grab all the pages with a custom setting
18
        $pages = Page::get()
19
            ->filter('NextReviewDate:LessThanOrEqual', $now->URLDate());
20
21
22
23
        // Calculate whether today is the date a First or Second review should occur
24
        $config = SiteConfig::current_site_config();
25
        $firstReview = $config->FirstReviewDaysBefore;
26
        $secondReview = $config->SecondReviewDaysBefore;
27
        // Subtract the number of days prior to the review, from the current date
28
29
        // Get all pages where the NextReviewDate is still in the future
30
        $pendingPages = Page::get()->filter('NextReviewDate:GreaterThan', $now->URLDate());
31
32
        // for each of these pages, check if today is the date the First or Second
33
        // reminder should be sent, and if so, add it to the appropriate ArrayList
34
        $firstReminderPages = new ArrayList();
35
        $secondReminderPages = new ArrayList();
36
37
        foreach ($pendingPages as $page) {
38
            $notifyDate1 = date('Y-m-d', strtotime($page->NextReviewDate . ' -' . $firstReview . ' days'));
39
            $notifyDate2 = date('Y-m-d', strtotime($page->NextReviewDate . ' -' . $secondReview . ' days'));
40
41
            if ($notifyDate1 == $now->URLDate()) {
42
                $firstReminderPages->push($page);
43
            }
44
            if ($notifyDate2 == $now->URLDate()) {
45
                $secondReminderPages->push($page);
46
            }
47
        }
48
49
        $overduePages = $this->getNotifiablePagesForOwners($pages);
50
51
        // Send one email to one owner with all the pages in there instead of no of pages of emails.
52
        foreach ($overduePages as $memberID => $pages) {
0 ignored issues
show
Unused Code introduced by
This foreach statement is empty and can be removed.

This check looks for foreach loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
53
            //$this->notifyOwner($memberID, $pages, "due");
0 ignored issues
show
Unused Code Comprehensibility introduced by
77% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
54
        }
55
56
        // Send an email to the generic address with any first or second reminders
57
        $this->notifyTeam($firstReminderPages, $secondReminderPages);
58
        die();
0 ignored issues
show
Coding Style Compatibility introduced by
The method run() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
59
60
        ContentReviewCompatability::done($compatibility);
0 ignored issues
show
Unused Code introduced by
\ContentReviewCompatability::done($compatibility); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
61
    }
62
63
    /**
64
     * @param SS_list $pages
65
     *
66
     * @return array
67
     */
68
    protected function getNotifiablePagesForOwners(SS_list $pages)
69
    {
70
        $overduePages = array();
71
72
        foreach ($pages as $page) {
73
74
            if (!$page->canRemind()) {
75
                continue;
76
            }
77
            
78
            $option = $page->getOptions();
79
80
            foreach ($option->ContentReviewOwners() as $owner) {
81
                if (!isset($overduePages[$owner->ID])) {
82
                    $overduePages[$owner->ID] = new ArrayList();
83
                }
84
85
                $overduePages[$owner->ID]->push($page);
86
            }
87
        }
88
89
        return $overduePages;
90
    }
91
    
92
    /**
93
     * Send an email to the configured team indicating which notices are at 'first reminder' or 'second reminder' status
94
     * The 'days before due' value for each of these is configurable in settings.
95
     * ie. a value of 30 for the 'first reminder' setting means a page with a review date exactly 30 days from due, will
96
     * be present in the email sent to the configured address.
97
     */
98
    protected function notifyTeam($firstReminderPages, $secondReminderPages) {
99
        // Prepare variables
100
        $siteConfig = SiteConfig::current_site_config();
101
        $templateVariables1 = $this->getTemplateVariables('', $siteConfig, $firstReminderPages, 'reminder1');
0 ignored issues
show
Documentation introduced by
'' is of type string, but the function expects a object<Member>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Unused Code introduced by
The call to ContentReviewEmails::getTemplateVariables() has too many arguments starting with 'reminder1'.

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...
102
        $templateVariables2 = $this->getTemplateVariables('', $siteConfig, $secondReminderPages, 'reminder2');
0 ignored issues
show
Documentation introduced by
'' is of type string, but the function expects a object<Member>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Unused Code introduced by
The call to ContentReviewEmails::getTemplateVariables() has too many arguments starting with 'reminder2'.

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...
103
104
        // Build email
105
        $email = new Email();
106
        $email->setTo($siteConfig->ReviewReminderEmail);
107
        $email->setFrom($siteConfig->ReviewFrom);
108
109
        $subject = $siteConfig->ReviewSubjectReminder;
110
        $email->setSubject($subject);
111
112
        // Get user-editable body
113
        $bodyFirstReminder = $this->getEmailBody($siteConfig, $templateVariables1, 'reminder1');
114
        $bodySecondReminder = $this->getEmailBody($siteConfig, $templateVariables2, 'reminder2');
115
116
        // Populate mail body with fixed template
117
        $email->setTemplate($siteConfig->config()->content_review_reminder_template);
118
        $email->populateTemplate($templateVariables1, $templateVariables2);
0 ignored issues
show
Unused Code introduced by
The call to Email::populateTemplate() has too many arguments starting with $templateVariables2.

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...
119
        $email->populateTemplate(array(
120
            'EmailBodyFirstReminder' => $bodyFirstReminder,
121
            'EmailBodySecondReminder' => $bodySecondReminder,
122
            'FirstReminderPages' => $firstReminderPages,
123
            'SecondReminderPages' => $secondReminderPages,
124
        ));
125
126
        $email->send();
127
    }
128
129
    /**
130
     * @param int           $ownerID
131
     * @param array|SS_List $pages
132
     * @param string        $type
133
     */
134
    protected function notifyOwner($ownerID, SS_List $pages, $type)
135
    {
136
        // Prepare variables
137
        $siteConfig = SiteConfig::current_site_config();
138
        $owner = Member::get()->byID($ownerID);
139
        $templateVariables = $this->getTemplateVariables($owner, $siteConfig, $pages, $type);
0 ignored issues
show
Bug introduced by
It seems like $owner defined by \Member::get()->byID($ownerID) on line 138 can also be of type object<DataObject>; however, ContentReviewEmails::getTemplateVariables() does only seem to accept object<Member>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Unused Code introduced by
The call to ContentReviewEmails::getTemplateVariables() has too many arguments starting with $type.

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...
140
141
        // Build email
142
        $email = new Email();
143
        $email->setTo($owner->Email);
144
        $email->setFrom($siteConfig->ReviewFrom);
145
146
        $subject = $siteConfig->ReviewSubject;
147
        $email->setSubject($subject);
148
149
        // Get user-editable body
150
        $body = $this->getEmailBody($siteConfig, $templateVariables, $type);
151
152
        // Populate mail body with fixed template
153
        $email->setTemplate($siteConfig->config()->content_review_template);
154
        $email->populateTemplate($templateVariables);
155
        $email->populateTemplate(array(
156
            'EmailBody' => $body,
157
            'Recipient' => $owner,
158
            'Pages' => $pages,
159
        ));
160
161
        $email->send();
162
    }
163
164
    /**
165
     * Get string value of HTML body with all variable evaluated.
166
     *
167
     * @param SiteConfig $config
168
     * @param array List of safe template variables to expose to this template
169
     *
170
     * @return HTMLText
171
     */
172
    protected function getEmailBody($config, $variables, $type)
173
    {
174
        if ($type == "reminder1") {
175
            $template = SSViewer::fromString($config->ReviewBodyFirstReminder);
176
        }
177
        if ($type == "reminder2") {
178
            $template = SSViewer::fromString($config->ReviewBodySecondReminder);
179
        }
180
        if ($type == "due") {
181
            $template = SSViewer::fromString($config->ReviewBody);
182
        }
183
        
184
        $value = $template->process(new ArrayData($variables));
0 ignored issues
show
Bug introduced by
The variable $template does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
185
186
        // Cast to HTML
187
        return DBField::create_field('HTMLText', (string) $value);
188
    }
189
190
    /**
191
     * Gets list of safe template variables and their values which can be used
192
     * in both the static and editable templates.
193
     *
194
     * {@see ContentReviewAdminHelp.ss}
195
     *
196
     * @param Member     $recipient
197
     * @param SiteConfig $config
198
     * @param SS_List    $pages
199
     * @param string     $type
0 ignored issues
show
Bug introduced by
There is no parameter named $type. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
200
     *
201
     * @return array
202
     */
203
    protected function getTemplateVariables($recipient = null, $config, $pages)
204
    {
205
        if ($recipient != null) {
206
            return array(
207
                'Subject' => $config->ReviewSubject,
208
                'PagesCount' => $pages->count(),
209
                'FromEmail' => $config->ReviewFrom,
210
                'ToFirstName' => $recipient->FirstName,
211
                'ToSurname' => $recipient->Surname,
212
                'ToEmail' => $recipient->Email
213
            );
214
        } else {
215
            return array(
216
                'Subject' => $config->ReviewSubjectReminder,
217
                'FirstReminderPagesCount' => $pages->count(),
218
                'SecondReminderPagesCount' => $pages->count(),
219
                'FromEmail' => $config->ReviewFrom,
220
                'ToEmail' => $config->ReviewReminderEmail
221
            );
222
223
        }
224
225
226
227
    }
228
}
229