GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — integration ( 45cc9f...98bc42 )
by Brendan
05:52
created

SectionEvent::processSendMailFilter()   F

Complexity

Conditions 15
Paths 1020

Size

Total Lines 112
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
eloc 61
nc 1020
nop 5
dl 0
loc 112
rs 2
c 1
b 0
f 0

How to fix   Long Method    Complexity   

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
 * @package events
5
 */
6
/**
7
 * The `SectionEvent` class provides methods required to save
8
 * data entered on the frontend to a corresponding Symphony section.
9
 *
10
 * @since Symphony 2.3.1
11
 * @link http://getsymphony.com/learn/concepts/view/events/
12
 */
13
14
abstract class SectionEvent extends Event
15
{
16
    /**
17
     * An associative array of results from the filters that have run
18
     * on this event.
19
     * @var array
20
     */
21
    public $filter_results = array();
22
23
    /**
24
     * An associative array of errors from the filters that have run
25
     * on this event.
26
     * @var array
27
     */
28
    public $filter_errors = array();
29
30
    /**
31
     * This method will construct XML that represents the result of
32
     * an Event filter.
33
     *
34
     * @param string $name
35
     *  The name of the filter
36
     * @param string $status
37
     *  The status of the filter, either passed or failed.
38
     * @param XMLElement|string $message
39
     *  Optionally, an XMLElement or string to be appended to this
40
     *  `<filter>` element. XMLElement allows for more complex return
41
     *  types.
42
     * @param array $attributes
43
     *  An associative array of additional attributes to add to this
44
     *  `<filter>` element
45
     * @return XMLElement
46
     */
47
    public static function buildFilterElement($name, $status, $message = null, array $attributes = null)
48
    {
49
        $filter = new XMLElement('filter', (!$message || is_object($message) ? null : $message), array('name' => $name, 'status' => $status));
50
51
        if ($message instanceof XMLElement) {
52
            $filter->appendChild($message);
53
        }
54
55
        if (is_array($attributes)) {
56
            $filter->setAttributeArray($attributes);
57
        }
58
59
        return $filter;
60
    }
61
62
    /**
63
     * Appends errors generated from fields during the execution of an Event
64
     *
65
     * @param XMLElement $result
66
     * @param array $fields
67
     * @param array $errors
68
     * @param object $post_values
69
     * @throws Exception
70
     * @return XMLElement
71
     */
72
    public static function appendErrors(XMLElement $result, array $fields, $errors, $post_values)
0 ignored issues
show
Coding Style introduced by
appendErrors uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
73
    {
74
        $result->setAttribute('result', 'error');
75
        $result->appendChild(new XMLElement('message', __('Entry encountered errors when saving.'), array(
76
            'message-id' => EventMessages::ENTRY_ERRORS
77
        )));
78
79
        foreach ($errors as $field_id => $message) {
80
            $field = FieldManager::fetch($field_id);
81
82
            // Do a little bit of a check for files so that we can correctly show
83
            // whether they are 'missing' or 'invalid'. If it's missing, then we
84
            // want to remove the data so `__reduceType` will correctly resolve to
85
            // missing instead of invalid.
86
            // @see https://github.com/symphonists/s3upload_field/issues/17
87
            if (isset($_FILES['fields']['error'][$field->get('element_name')])) {
0 ignored issues
show
Bug introduced by
The method get cannot be called on $field (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
88
                $upload = $_FILES['fields']['error'][$field->get('element_name')];
0 ignored issues
show
Bug introduced by
The method get cannot be called on $field (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
89
90
                if ($upload === UPLOAD_ERR_NO_FILE) {
91
                    unset($fields[$field->get('element_name')]);
92
                }
93
            }
94
95
            if (is_array($fields[$field->get('element_name')])) {
0 ignored issues
show
Bug introduced by
The method get cannot be called on $field (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
96
                $type = array_reduce($fields[$field->get('element_name')], array('SectionEvent', '__reduceType'));
0 ignored issues
show
Bug introduced by
The method get cannot be called on $field (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
97
            } else {
98
                $type = ($fields[$field->get('element_name')] === '') ? 'missing' : 'invalid';
0 ignored issues
show
Bug introduced by
The method get cannot be called on $field (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
99
            }
100
101
            $error = self::createError($field, $type, $message);
0 ignored issues
show
Documentation introduced by
$field is of type array, but the function expects a object<Field>.

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...
102
            $result->appendChild($error);
103
        }
104
105
        if (isset($post_values) && is_object($post_values)) {
106
            $result->appendChild($post_values);
107
        }
108
109
        return $result;
110
    }
111
112
    /**
113
     * Given a Field instance, the type of error, and the message, this function
114
     * creates an XMLElement node so that it can be added to the `?debug` for the
115
     * Event
116
     *
117
     * @since Symphony 2.5.0
118
     * @param Field $field
119
     * @param string $type
120
     *  At the moment 'missing' or 'invalid' accepted
121
     * @param string $message
122
     * @return XMLElement
123
     */
124
    public static function createError(Field $field, $type, $message = null)
125
    {
126
        $error = new XMLElement($field->get('element_name'), null, array(
127
            'label' => General::sanitize($field->get('label')),
128
            'type' => $type,
129
            'message-id' => ($type === 'missing') ? EventMessages::FIELD_MISSING : EventMessages::FIELD_INVALID,
130
            'message' => General::sanitize($message)
131
        ));
132
133
        return $error;
134
    }
135
136
    /**
137
     * This function searches the `$haystack` for the given `$needle`,
138
     * where the needle is a string representation of where the desired
139
     * value exists in the `$haystack` array. For example `fields[name]`
140
     * would look in the `$haystack` for the key of `fields` that has the
141
     * key `name` and return the value.
142
     *
143
     * @param string $needle
144
     *  The needle, ie. `fields[name]`.
145
     * @param array $haystack
146
     *  Associative array to find the needle, ie.
147
     *      `array('fields' => array(
148
     *          'name' => 'Bob',
149
     *          'age' => '10'
150
     *      ))`
151
     * @param string $default
152
     *  If the `$needle` is not found, return this value. Defaults to null.
153
     * @param boolean $discard_field_name
154
     *  When matches are found in the `$haystack`, they are added to results
155
     *  array. This parameter defines if this should be an associative array
156
     *  or just an array of the matches. Used in conjunction with `$collapse`
157
     * @param boolean $collapse
158
     *  If multiple values are found, this will cause them to be reduced
159
     *  to single string with ' ' as the separator. Defaults to true.
160
     * @return string|array
161
     */
162
    public static function replaceFieldToken($needle, $haystack, $default = null, $discard_field_name = true, $collapse = true)
163
    {
164
        if (preg_match('/^(fields\[[^\]]+\],?)+$/i', $needle)) {
165
            $parts = preg_split('/\,/i', $needle, -1, PREG_SPLIT_NO_EMPTY);
166
            $parts = array_map('trim', $parts);
167
168
            $stack = array();
169
170
            foreach ($parts as $p) {
171
                $field = str_replace(array('fields[', ']'), '', $p);
172
                ($discard_field_name ? $stack[] = $haystack[$field] : $stack[$field] = $haystack[$field]);
173
            }
174
175
            if (is_array($stack) && !empty($stack)) {
176
                return $collapse ? implode(' ', $stack) : $stack;
177
            } else {
178
                $needle = null;
179
            }
180
        }
181
182
        $needle = trim($needle);
183
184
        if (empty($needle)) {
185
            return $default;
186
        } else {
187
            return $needle;
188
        }
189
    }
190
191
    /**
192
     * Helper method to determine if a field is missing, or if the data
193
     * provided was invalid. Used in conjunction with `array_reduce`.
194
     *
195
     * @param array $a,
0 ignored issues
show
Bug introduced by
There is no parameter named $a,. 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...
196
     * @param array $b
197
     * @return string
198
     *  'missing' or 'invalid'
199
     */
200
    public function __reduceType($a, $b)
201
    {
202
        if (is_array($b)) {
203
            return array_reduce($b, array('SectionEvent', '__reduceType'));
204
        }
205
206
        return (strlen(trim($b)) === 0) ? 'missing' : 'invalid';
207
    }
208
209
    /**
210
     * This function will process the core Filters, Admin Only and Expect
211
     * Multiple, before invoking the `__doit` function, which actually
212
     * processes the Event. Once the Event has executed, this function will
213
     * determine if the user should be redirected to a URL, or to just return
214
     * the XML.
215
     *
216
     * @throws Exception
217
     * @return XMLElement|void
218
     *  If `$_REQUEST{'redirect']` is set, and the Event executed successfully,
219
     *  the user will be redirected to the given location. If `$_REQUEST['redirect']`
220
     *  is not set, or the Event encountered errors, an XMLElement of the Event
221
     *  result will be returned.
222
     */
223
    public function execute()
224
    {
225
        if (!isset($this->eParamFILTERS) || !is_array($this->eParamFILTERS)) {
226
            $this->eParamFILTERS = array();
0 ignored issues
show
Bug introduced by
The property eParamFILTERS does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
227
        }
228
229
        $result = new XMLElement($this->ROOTELEMENT);
0 ignored issues
show
Bug introduced by
The property ROOTELEMENT does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
230
231
        if (in_array('admin-only', $this->eParamFILTERS) && !Symphony::Engine()->isLoggedIn()) {
232
            $result->setAttribute('result', 'error');
233
            $result->appendChild(new XMLElement('message', __('Entry encountered errors when saving.'), array(
234
                'message-id' => EventMessages::ENTRY_ERRORS
235
            )));
236
            $result->appendChild(self::buildFilterElement('admin-only', 'failed'));
237
            return $result;
238
        }
239
240
        $entry_id = $position = $fields = null;
241
        $post = General::getPostData();
242
        $success = true;
243
        if (!is_array($post['fields'])) {
244
            $post['fields'] = array();
245
        }
246
247
        if (in_array('expect-multiple', $this->eParamFILTERS)) {
248
            foreach ($post['fields'] as $position => $fields) {
249
                if (isset($post['id'][$position]) && is_numeric($post['id'][$position])) {
250
                    $entry_id = $post['id'][$position];
251
                } else {
252
                    $entry_id = null;
253
                }
254
255
                $entry = new XMLElement('entry', null, array('position' => $position));
256
257
                // Reset errors for each entry execution
258
                $this->filter_results = $this->filter_errors = array();
259
260
                // Ensure that we are always dealing with an array.
261
                if (!is_array($fields)) {
262
                    $fields = array();
263
                }
264
265
                // Execute the event for this entry
266
                if (!$this->__doit($fields, $entry, $position, $entry_id)) {
267
                    $success = false;
268
                }
269
270
                $result->appendChild($entry);
271
            }
272
        } else {
273
            $fields = $post['fields'];
274
275
            if (isset($post['id']) && is_numeric($post['id'])) {
276
                $entry_id = $post['id'];
277
            }
278
279
            $success = $this->__doit($fields, $result, null, $entry_id);
280
        }
281
282
        if ($success && isset($_REQUEST['redirect'])) {
283
            redirect($_REQUEST['redirect']);
284
        }
285
286
        return $result;
287
    }
288
289
    /**
290
     * This function does the bulk of processing the Event, from running the delegates
291
     * to validating the data and eventually saving the data into Symphony. The result
292
     * of the Event is returned via the `$result` parameter.
293
     *
294
     * @param array $fields
295
     *  An array of $_POST data, to process and add/edit an entry.
296
     * @param XMLElement $result
297
     *  The XMLElement contains the result of the Event, it is passed by
298
     *  reference.
299
     * @param integer $position
300
     *  When the Expect Multiple filter is added, this event should expect
301
     *  to deal with adding (or editing) multiple entries at once.
302
     * @param integer $entry_id
303
     *  If this Event is editing an existing entry, that Entry ID will
304
     *  be passed to this function.
305
     * @throws Exception
306
     * @return XMLElement
307
     *  The result of the Event
308
     */
309
    public function __doit(array $fields = array(), XMLElement &$result, $position = null, $entry_id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $position 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...
Coding Style introduced by
__doit uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
310
    {
311
        $post_values = new XMLElement('post-values');
312
313
        if (!is_array($this->eParamFILTERS)) {
314
            $this->eParamFILTERS = array();
315
        }
316
317
        // Check to see if the Section of this Event is valid.
318
        if (!$section = SectionManager::fetch($this->getSource())) {
319
            $result->setAttribute('result', 'error');
320
            $result->appendChild(new XMLElement('message', __('The Section, %s, could not be found.', array($this->getSource())), array(
321
                'message-id' => EventMessages::SECTION_MISSING
322
            )));
323
            return false;
324
        }
325
326
        // Create the post data element
327
        if (!empty($fields)) {
328
            General::array_to_xml($post_values, $fields, true);
329
        }
330
331
        // If the EventPreSaveFilter fails, return early
332
        if ($this->processPreSaveFilters($result, $fields, $post_values, $entry_id) === false) {
333
            return false;
334
        }
335
336
        // If the `$entry_id` is provided, check to see if it exists.
337
        // @todo If this was moved above PreSaveFilters, we can pass the
338
        // Entry object to the delegate meaning extensions don't have to
339
        // do that step.
340
        if (isset($entry_id)) {
341
            $entry = EntryManager::fetch($entry_id);
342
            $entry = $entry[0];
343
344 View Code Duplication
            if (!is_object($entry)) {
345
                $result->setAttribute('result', 'error');
346
                $result->appendChild(new XMLElement('message', __('The Entry, %s, could not be found.', array($entry_id)), array(
347
                    'message-id' => EventMessages::ENTRY_MISSING
348
                )));
349
350
                return false;
351
            }
352
353
            // `$entry_id` wasn't provided, create a new Entry object.
354
        } else {
355
            $entry = EntryManager::create();
356
            $entry->set('section_id', $this->getSource());
357
        }
358
359
        // Validate the data. `$entry->checkPostData` loops over all fields calling
360
        // their `checkPostFieldData` function. If the return of the function is
361
        // `Entry::__ENTRY_FIELD_ERROR__` then abort the event and add the error
362
        // messages to the `$result`.
363
        if (Entry::__ENTRY_FIELD_ERROR__ === $entry->checkPostData($fields, $errors, ($entry->get('id') ? true : false))) {
364
            $result = self::appendErrors($result, $fields, $errors, $post_values);
0 ignored issues
show
Bug introduced by
The variable $errors does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
365
            return false;
366
367
            // If the data is good, process the data, almost ready to save it to the
368
            // Database. If processing fails, abort the event and display the errors
369
        } elseif (Entry::__ENTRY_OK__ !== $entry->setDataFromPost($fields, $errors, false, ($entry->get('id') ? true : false))) {
370
            $result = self::appendErrors($result, $fields, $errors, $post_values);
371
            return false;
372
373
            // Data is checked, data has been processed, by trying to save the
374
            // Entry caused an error to occur, so abort and return.
375
        } elseif ($entry->commit() === false) {
376
            $result->setAttribute('result', 'error');
377
            $result->appendChild(new XMLElement('message', __('Unknown errors where encountered when saving.'), array(
378
                'message-id' => EventMessages::UNKNOWN_ERROR
379
            )));
380
381
            if (isset($post_values) && is_object($post_values)) {
382
                $result->appendChild($post_values);
383
            }
384
385
            return false;
386
387
            // Entry was created, add the good news to the return `$result`
388
        } else {
389
            $result->setAttributeArray(array(
390
                'result' => 'success',
391
                'type' => (isset($entry_id) ? 'edited' : 'created'),
392
                'id' => $entry->get('id')
393
            ));
394
395
            if (isset($entry_id)) {
396
                $result->appendChild(new XMLElement('message', __('Entry edited successfully.'), array(
397
                    'message-id' => EventMessages::ENTRY_EDITED_SUCCESS
398
                )));
399
            } else {
400
                $result->appendChild(new XMLElement('message', __('Entry created successfully.'), array(
401
                    'message-id' => EventMessages::ENTRY_CREATED_SUCCESS
402
                )));
403
            }
404
        }
405
406
        // PASSIVE FILTERS ONLY AT THIS STAGE. ENTRY HAS ALREADY BEEN CREATED.
407
        if (in_array('send-email', $this->eParamFILTERS) && !in_array('expect-multiple', $this->eParamFILTERS)) {
408
            $result = $this->processSendMailFilter($result, $_POST['send-email'], $fields, $section, $entry);
0 ignored issues
show
Bug introduced by
It seems like $section defined by \SectionManager::fetch($this->getSource()) on line 318 can also be of type array; however, SectionEvent::processSendMailFilter() does only seem to accept object<Section>, 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...
409
        }
410
411
        $result = $this->processPostSaveFilters($result, $fields, $entry);
412
        $result = $this->processFinalSaveFilters($result, $fields, $entry);
413
414
        if (isset($post_values) && is_object($post_values)) {
415
            $result->appendChild($post_values);
416
        }
417
418
        return true;
419
    }
420
421
    /**
422
     * Processes all extensions attached to the `EventPreSaveFilter` delegate
423
     *
424
     * @uses EventPreSaveFilter
425
     *
426
     * @param XMLElement $result
427
     * @param array $fields
428
     * @param XMLElement $post_values
429
     * @param integer $entry_id
430
     * @return boolean
431
     */
432
    protected function processPreSaveFilters(XMLElement $result, array &$fields, XMLElement &$post_values, $entry_id = null)
433
    {
434
        $can_proceed = true;
435
436
        /**
437
         * Prior to saving entry from the front-end. This delegate will
438
         * force the Event to terminate if it populates the `$filter_results`
439
         * array. All parameters are passed by reference.
440
         *
441
         * @delegate EventPreSaveFilter
442
         * @param string $context
443
         * '/frontend/'
444
         * @param array $fields
445
         * @param Event $this
446
         * @param array $messages
447
         *  An associative array of array's which contain 4 values,
448
         *  the name of the filter (string), the status (boolean),
449
         *  the message (string) an optionally an associative array
450
         *  of additional attributes to add to the filter element.
451
         * @param XMLElement $post_values
452
         * @param integer $entry_id
453
         *  If editing an entry, this parameter will be an integer,
454
         *  otherwise null.
455
         */
456
        Symphony::ExtensionManager()->notifyMembers(
457
            'EventPreSaveFilter',
458
            '/frontend/',
459
            array(
460
                'fields' => &$fields,
461
                'event' => &$this,
462
                'messages' => &$this->filter_results,
463
                'post_values' => &$post_values,
464
                'entry_id' => $entry_id
465
            )
466
        );
467
468
        // Logic taken from `event.section.php` to fail should any `$this->filter_results`
469
        // be returned. This delegate can cause the event to exit early.
470
        if (is_array($this->filter_results) && !empty($this->filter_results)) {
471
            $can_proceed = true;
472
473
            foreach ($this->filter_results as $fr) {
474
                list($name, $status, $message, $attributes) = $fr;
475
476
                $result->appendChild(
477
                    self::buildFilterElement($name, ($status ? 'passed' : 'failed'), $message, $attributes)
478
                );
479
480
                if ($status === false) {
481
                    $can_proceed = false;
482
                }
483
            }
484
485 View Code Duplication
            if ($can_proceed !== true) {
486
                $result->appendChild($post_values);
487
                $result->setAttribute('result', 'error');
488
                $result->appendChild(new XMLElement('message', __('Entry encountered errors when saving.'), array(
489
                    'message-id' => EventMessages::FILTER_FAILED
490
                )));
491
            }
492
        }
493
494
        // Reset the filter results to prevent duplicates. RE: #2179
495
        $this->filter_results = array();
496
        return $can_proceed;
497
    }
498
499
    /**
500
     * Processes all extensions attached to the `EventPostSaveFilter` delegate
501
     *
502
     * @uses EventPostSaveFilter
503
     *
504
     * @param XMLElement $result
505
     * @param array $fields
506
     * @param Entry $entry
507
     * @return XMLElement
508
     */
509 View Code Duplication
    protected function processPostSaveFilters(XMLElement $result, array $fields, Entry $entry = null)
510
    {
511
        /**
512
         * After saving entry from the front-end. This delegate will not force
513
         * the Events to terminate if it populates the `$filter_results` array.
514
         * Provided with references to this object, the `$_POST` data and also
515
         * the error array
516
         *
517
         * @delegate EventPostSaveFilter
518
         * @param string $context
519
         * '/frontend/'
520
         * @param integer $entry_id
521
         * @param array $fields
522
         * @param Entry $entry
523
         * @param Event $this
524
         * @param array $messages
525
         *  An associative array of array's which contain 4 values,
526
         *  the name of the filter (string), the status (boolean),
527
         *  the message (string) an optionally an associative array
528
         *  of additional attributes to add to the filter element.
529
         */
530
        Symphony::ExtensionManager()->notifyMembers('EventPostSaveFilter', '/frontend/', array(
531
            'entry_id' => $entry->get('id'),
0 ignored issues
show
Bug introduced by
It seems like $entry is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
532
            'fields' => $fields,
533
            'entry' => $entry,
534
            'event' => &$this,
535
            'messages' => &$this->filter_results
536
        ));
537
538
        if (is_array($this->filter_results) && !empty($this->filter_results)) {
539
            foreach ($this->filter_results as $fr) {
540
                list($name, $status, $message, $attributes) = $fr;
541
542
                $result->appendChild(
543
                    self::buildFilterElement($name, ($status ? 'passed' : 'failed'), $message, $attributes)
544
                );
545
            }
546
        }
547
548
        // Reset the filter results to prevent duplicates. RE: #2179
549
        $this->filter_results = array();
550
        return $result;
551
    }
552
553
    /**
554
     * Processes all extensions attached to the `EventFinalSaveFilter` delegate
555
     *
556
     * @uses EventFinalSaveFilter
557
     *
558
     * @param XMLElement $result
559
     * @param array $fields
560
     * @param Entry $entry
561
     * @return XMLElement
562
     */
563 View Code Duplication
    protected function processFinalSaveFilters(XMLElement $result, array $fields, Entry $entry = null)
564
    {
565
        /**
566
         * This delegate that lets extensions know the final status of the
567
         * current Event. It is triggered when everything has processed correctly.
568
         * The `$messages` array contains the results of the previous filters that
569
         * have executed, and the `$errors` array contains any errors that have
570
         * occurred as a result of this delegate. These errors cannot stop the
571
         * processing of the Event, as that has already been done.
572
         *
573
         *
574
         * @delegate EventFinalSaveFilter
575
         * @param string $context
576
         * '/frontend/'
577
         * @param array $fields
578
         * @param Event $this
579
         * @param array $messages
580
         *  An associative array of array's which contain 4 values,
581
         *  the name of the filter (string), the status (boolean),
582
         *  the message (string) an optionally an associative array
583
         *  of additional attributes to add to the filter element.
584
         * @param array $errors
585
         *  An associative array of array's which contain 4 values,
586
         *  the name of the filter (string), the status (boolean),
587
         *  the message (string) an optionally an associative array
588
         *  of additional attributes to add to the filter element.
589
         * @param Entry $entry
590
         */
591
        Symphony::ExtensionManager()->notifyMembers('EventFinalSaveFilter', '/frontend/', array(
592
            'fields'    => $fields,
593
            'event'     => $this,
594
            'messages'  => $this->filter_results,
595
            'errors'    => &$this->filter_errors,
596
            'entry'     => $entry
597
        ));
598
599
        if (is_array($this->filter_errors) && !empty($this->filter_errors)) {
600
            foreach ($this->filter_errors as $fr) {
601
                list($name, $status, $message, $attributes) = $fr;
602
603
                $result->appendChild(
604
                    self::buildFilterElement($name, ($status ? 'passed' : 'failed'), $message, $attributes)
605
                );
606
            }
607
        }
608
609
        // Reset the filter results to prevent duplicates. RE: #2179
610
        $this->filter_results = array();
611
        return $result;
612
    }
613
614
    /**
615
     * This function handles the Send Mail filter which will send an email
616
     * to each specified recipient informing them that an Entry has been
617
     * created.
618
     *
619
     * @param XMLElement $result
620
     *  The XMLElement of the XML that is going to be returned as part
621
     *  of this event to the page.
622
     * @param array $send_email
623
     *  Associative array of `send-mail` parameters.*  Associative array of `send-mail` parameters.
624
     * @param array $fields
625
     *  Array of post data to extract the values from
626
     * @param Section $section
627
     *  This current Entry that has just been updated or created
628
     * @param Entry $entry
629
     * @throws Exception
630
     * @return XMLElement
631
     *  The modified `$result` with the results of the filter.
632
     */
633
    public function processSendMailFilter(XMLElement $result, array $send_email, array &$fields, Section $section, Entry $entry)
634
    {
635
        $fields['recipient']        = self::replaceFieldToken($send_email['recipient'], $fields);
636
        $fields['recipient']        = preg_split('/\,/i', $fields['recipient'], -1, PREG_SPLIT_NO_EMPTY);
637
        $fields['recipient']        = array_map('trim', $fields['recipient']);
638
639
        $fields['subject']          = self::replaceFieldToken($send_email['subject'], $fields, __('[Symphony] A new entry was created on %s', array(Symphony::Configuration()->get('sitename', 'general'))));
640
        $fields['body']             = self::replaceFieldToken($send_email['body'], $fields, null, false, false);
641
        $fields['sender-email']     = self::replaceFieldToken($send_email['sender-email'], $fields);
642
        $fields['sender-name']      = self::replaceFieldToken($send_email['sender-name'], $fields);
643
644
        $fields['reply-to-name']    = self::replaceFieldToken($send_email['reply-to-name'], $fields);
645
        $fields['reply-to-email']   = self::replaceFieldToken($send_email['reply-to-email'], $fields);
646
647
        $edit_link = SYMPHONY_URL . '/publish/' . $section->get('handle') . '/edit/' . $entry->get('id').'/';
648
        $language = Symphony::Configuration()->get('lang', 'symphony');
649
        $template_path = Event::getNotificationTemplate($language);
0 ignored issues
show
Bug introduced by
It seems like $language defined by \Symphony::Configuration...get('lang', 'symphony') on line 648 can also be of type array; however, Event::getNotificationTemplate() does only seem to accept string, 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...
650
        $body = sprintf(file_get_contents($template_path), $section->get('name'), $edit_link);
651
652
        if (is_array($fields['body'])) {
653
            foreach ($fields['body'] as $field_handle => $value) {
654
                $body .= "// $field_handle" . PHP_EOL . $value . PHP_EOL . PHP_EOL;
655
            }
656
        } else {
657
            $body .= $fields['body'];
658
        }
659
660
        // Loop over all the recipients and attempt to send them an email
661
        // Errors will be appended to the Event XML
662
        $errors = array();
663
664
        foreach ($fields['recipient'] as $recipient) {
665
            $author = AuthorManager::fetchByUsername($recipient);
666
667
            if (empty($author)) {
668
                $errors['recipient'][$recipient] = __('Recipient not found');
669
                continue;
670
            }
671
672
            $email = Email::create();
673
674
            // Huib: Exceptions are also thrown in the settings functions, not only in the send function.
675
            // Those Exceptions should be caught too.
676
            try {
677
                $email->recipients = array(
0 ignored issues
show
Bug introduced by
The property recipients does not seem to exist. Did you mean _recipients?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
678
                    $author->get('first_name') => $author->get('email')
679
                );
680
681
                if ($fields['sender-name'] !== null) {
682
                    $email->sender_name = $fields['sender-name'];
0 ignored issues
show
Bug introduced by
The property sender_name does not seem to exist. Did you mean _sender_name?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
683
                }
684
685
                if ($fields['sender-email'] !== null) {
686
                    $email->sender_email_address = $fields['sender-email'];
0 ignored issues
show
Bug introduced by
The property sender_email_address does not seem to exist. Did you mean _sender_email_address?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
687
                }
688
689
                if ($fields['reply-to-name'] !== null) {
690
                    $email->reply_to_name = $fields['reply-to-name'];
0 ignored issues
show
Bug introduced by
The property reply_to_name does not seem to exist. Did you mean _reply_to_name?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
691
                }
692
693
                if ($fields['reply-to-email'] !== null) {
694
                    $email->reply_to_email_address = $fields['reply-to-email'];
0 ignored issues
show
Bug introduced by
The property reply_to_email_address does not seem to exist. Did you mean _reply_to_email_address?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
695
                }
696
697
                $email->text_plain = str_replace('<!-- RECIPIENT NAME -->', $author->get('first_name'), $body);
0 ignored issues
show
Bug introduced by
The property text_plain does not seem to exist. Did you mean _text_plain?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
698
                $email->subject = $fields['subject'];
0 ignored issues
show
Bug introduced by
The property subject does not seem to exist. Did you mean _subject?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
699
                $email->send();
700
            } catch (EmailValidationException $e) {
701
                $errors['address'][$author->get('email')] = $e->getMessage();
702
703
                // The current error array does not permit custom tags.
704
                // Therefore, it is impossible to set a "proper" error message.
705
                // Will return the failed email address instead.
706
            } catch (EmailGatewayException $e) {
707
                $errors['gateway'][$author->get('email')] = $e->getMessage();
708
709
                // Because we don't want symphony to break because it can not send emails,
710
                // all exceptions are logged silently.
711
                // Any custom event can change this behaviour.
712
            } catch (EmailException $e) {
713
                $errors['email'][$author->get('email')] = $e->getMessage();
714
            }
715
        }
716
717
        // If there were errors, output them to the event
718
        if (!empty($errors)) {
719
            $xml = self::buildFilterElement('send-email', 'failed');
720
721
            foreach ($errors as $type => $messages) {
722
                $xType = new XMLElement('error');
723
                $xType->setAttribute('error-type', $type);
724
725
                foreach ($messages as $recipient => $message) {
726
                    $xType->appendChild(
727
                        new XMLElement('message', General::wrapInCDATA($message), array(
728
                            'recipient' => $recipient
729
                        ))
730
                    );
731
                }
732
733
                $xml->appendChild($xType);
734
            }
735
736
            $result->appendChild($xml);
737
        } else {
738
            $result->appendChild(
739
                self::buildFilterElement('send-email', 'passed')
740
            );
741
        }
742
743
        return $result;
744
    }
745
}
746
747
/**
748
 * Basic lookup class for Event messages, allows for frontend developers
749
 * to localise and change event messages without relying on string
750
 * comparision.
751
 *
752
 * @since Symphony 2.4
753
 */
754
class EventMessages
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
755
{
756
    const UNKNOWN_ERROR = 0;
757
758
    const ENTRY_CREATED_SUCCESS = 100;
759
    const ENTRY_EDITED_SUCCESS = 101;
760
    const ENTRY_ERRORS = 102;
761
    const ENTRY_MISSING = 103;
762
    const ENTRY_NOT_UNIQUE = 104;
763
764
    const SECTION_MISSING = 200;
765
766
    const FIELD_MISSING = 301;
767
    const FIELD_INVALID = 302;
768
    const FIELD_NOT_UNIQUE = 303;
769
770
    const FILTER_FAILED = 400;
771
772
    const SECURITY_XSRF = 500;
773
}
774