Failed Conditions
Branch master (3ce7e2)
by Nick
14:43
created

Normal   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 297
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 297
rs 6.8539
wmc 54

9 Methods

Rating   Name   Duplication   Size   Complexity  
C find_constituency() 0 51 10
C generate_pagination() 0 60 9
A get_first_page_data() 0 13 4
A generate_pagination_links() 0 22 3
B find_members() 0 28 5
B get_sort_args() 0 16 5
A find_glossary_items() 0 17 4
C get_sidebar_links() 0 26 7
C search() 0 45 7

How to fix   Complexity   

Complex Class

Complex classes like Normal often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Normal, and based on these observations, apply Extract Interface, too.

1
<?php
2
# vim:sw=4:ts=4:et:nowrap
3
4
namespace MySociety\TheyWorkForYou\Search;
5
6
class Normal extends \MySociety\TheyWorkForYou\Search {
7
8
    private function get_sort_args() {
9
        $pagenum = get_http_var('p');
10
        if (!is_numeric($pagenum)) {
11
            $pagenum = 1;
12
        }
13
14
        $o = get_http_var('o');
15
        $args = array (
16
            's' => $this->searchstring,
17
            'p' => $pagenum,
18
            'num' => get_http_var('num'),
19
            'pop' => get_http_var('pop'),
20
            'o' => ($o=='d' || $o=='r' || $o=='o') ? $o : 'd',
21
        );
22
23
        return $args;
24
    }
25
26
    private function get_first_page_data($args) {
27
        $members = null;
28
        $cons = null;
29
        $glossary = null;
30
31
        $mp_types = array();
32
        if ($args['p'] == 1 && $args['s'] && !preg_match('#[a-z]+:[a-z0-9]+#', $args['s'])) {
33
            $members = $this->find_members();
34
            list($cons, $mp_types) = $this->find_constituency($args);
35
            $glossary = $this->find_glossary_items($args);
36
        }
37
38
        return array($members, $cons, $mp_types, $glossary);
39
    }
40
41
    public function search($searchstring) {
42
        global $DATA, $this_page, $SEARCHENGINE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
43
44
        $this->searchstring = $searchstring;
45
        $SEARCHENGINE = new \SEARCHENGINE($this->searchstring);
46
47
        $args = $this->get_sort_args();
48
49
        $pagenum = $args['p'];
50
51
        $DATA->set_page_metadata($this_page, 'rss', '/search/rss/?s=' . urlencode($this->searchstring));
52
        if ($pagenum == 1) {
53
            # Allow indexing of first page of search results
54
            $DATA->set_page_metadata($this_page, 'robots', '');
55
        }
56
57
        $sort_order = 'newest';
58
        if ( $args['o'] == 'o' ) {
59
            $sort_order = 'oldest';
60
        } else if ( $args['o'] == 'r' ) {
61
            $sort_order = 'relevance';
62
        }
63
64
        list($members, $cons, $mp_types, $glossary) = $this->get_first_page_data($args);
65
66
        if (!defined('FRONT_END_SEARCH') || !FRONT_END_SEARCH) {
1 ignored issue
show
Bug introduced by
The constant MySociety\TheyWorkForYou\Search\FRONT_END_SEARCH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
67
            return array(
68
                'error' =>'Apologies, search has been turned off currently for performance reasons.'
69
            );
70
        }
71
72
        if (!$SEARCHENGINE->valid) {
73
            return array('error' => $SEARCHENGINE->error);
74
        } else {
75
            $LIST = new \HANSARDLIST();
76
            $data = $LIST->display('search', $args , 'none');
77
            $data['search_type'] = 'normal';
78
            $data['sort_order'] = $sort_order;
79
            $data['members'] = $members;
80
            $data['cons'] = $cons;
81
            $data['mp_types'] = $mp_types;
82
            $data['glossary'] = $glossary;
83
            $data['pagination_links'] = $this->generate_pagination($data['info']);
84
            $data['search_sidebar'] = $this->get_sidebar_links($this->searchstring);
0 ignored issues
show
Unused Code introduced by
The call to MySociety\TheyWorkForYou...al::get_sidebar_links() has too many arguments starting with $this->searchstring. ( Ignorable by Annotation )

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

84
            /** @scrutinizer ignore-call */ 
85
            $data['search_sidebar'] = $this->get_sidebar_links($this->searchstring);

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. Please note the @ignore annotation hint above.

Loading history...
85
            return $data;
86
        }
87
    }
88
89
    private function find_constituency($args) {
90
        if ($args['s'] != '') {
91
            $searchterm = $args['s'];
92
        } else {
93
            return false;
94
        }
95
96
        list ($constituencies, ) = \MySociety\TheyWorkForYou\Utility\Search::searchConstituenciesByQuery($searchterm);
97
98
        $constituency = "";
99
        if (count($constituencies)==1) {
100
            $constituency = $constituencies[0];
101
        }
102
103
        $cons = array();
104
        $mp_types = array(
105
            'mp' => 0,
106
            'former' => 0
107
        );
108
109
        if ($constituency != '') {
110
            try {
111
            // Got a match, display....
112
113
                $MEMBER = new \MySociety\TheyWorkForYou\Member(array('constituency'=>$constituency, 'house' => 1));
114
                $cons[] = $MEMBER;
115
                if ( $MEMBER->current_member(1) ) {
116
                    $mp_types['mp']++;
117
                } else {
118
                    $mp_types['former']++;
119
                }
120
            } catch ( \MySociety\TheyWorkForYou\MemberException $e ) {
121
                $cons = array();
122
            }
123
        } elseif (count($constituencies)) {
124
            foreach ($constituencies as $constituency) {
125
                try {
126
                    $MEMBER = new \MySociety\TheyWorkForYou\Member(array('constituency'=>$constituency, 'house' => 1));
127
                    $cons[] = $MEMBER;
128
                    if ( $MEMBER->current_member(1) ) {
129
                        $mp_types['mp']++;
130
                    } else {
131
                        $mp_types['former']++;
132
                    }
133
                } catch ( \MySociety\TheyWorkForYou\MemberException $e ) {
134
                    continue;
135
                }
136
            }
137
        }
138
139
        return array($cons, $mp_types);
140
    }
141
142
    private function find_members() {
143
        $searchstring = trim(preg_replace('#-?[a-z]+:[a-z0-9]+#', '', $this->searchstring));
144
        if (!$searchstring) return array();
145
146
        $members = array();
147
148
        $q = \MySociety\TheyWorkForYou\Utility\Search::searchMemberDbLookup($searchstring);
149
        $row_count = $q->rows();
150
        for ($n=0; $n<$row_count; $n++) {
151
            $members[] = $q->field($n, 'person_id');
152
        }
153
154
        $db = new \ParlDB;
155
        $q = $db->query("SELECT person FROM moffice WHERE position LIKE :pos ORDER BY from_date DESC, moffice_id",
156
            array('pos' => "%$searchstring%"));
157
        $row_count = $q->rows();
158
        for ($n=0; $n<$row_count; $n++) {
159
            $members[] = $q->field($n, 'person');
160
        }
161
162
        // We might have duplicates, so get rid of them
163
        $members = array_unique($members);
164
165
        foreach ($members as $i => $pid) {
166
            $member = new \MySociety\TheyWorkForYou\Member(array('person_id' => $pid));
167
            $members[$i] = $member;
168
        }
169
        return $members;
170
    }
171
172
    private function find_glossary_items($args) {
173
        $GLOSSARY = new \GLOSSARY($args);
174
        $items = array();
175
176
        if (isset($GLOSSARY->num_search_matches) && $GLOSSARY->num_search_matches >= 1) {
177
            $URL = new \MySociety\TheyWorkForYou\Url('glossary');
178
            $URL->insert(array('gl' => ""));
179
            foreach ($GLOSSARY->search_matches as $glossary_id => $term) {
180
                $URL->update(array("gl" => $glossary_id));
181
                $items[] = array(
182
                    'url' => $URL->generate(),
183
                    'term' => $term['title'],
184
                    'body' => $term['body']
185
                );
186
            }
187
        }
188
        return $items;
189
    }
190
191
    private function generate_pagination_links($data, $url, $first, $last) {
192
        $links = array();
193
194
        for ($n = $first; $n <= $last; $n++) {
195
196
            if ($n > 1) {
197
                $url->insert(array('p'=>$n));
198
            } else {
199
                // No page number for the first page.
200
                $url->remove(array('p'));
201
            }
202
203
            $link = array(
204
                'url' => $url->generate(),
205
                'page' => $n,
206
                'current' => ( $n == $data['page'] )
207
            );
208
209
            $links[] = $link;
210
        }
211
212
        return $links;
213
    }
214
215
    private function generate_pagination($data) {
216
        $total_results      = $data['total_results'];
217
        $results_per_page   = $data['results_per_page'];
218
        $page               = $data['page'];
219
        $pagelinks          = array();
220
221
        $URL = $this->get_search_url($data);
222
223
        if ($total_results > $results_per_page) {
224
225
            $numpages = ceil($total_results / $results_per_page);
226
227
            // How many links are we going to display on the page - don't want to
228
            // display all of them if we have 100s...
229
            if ($page < 10) {
230
                $firstpage = 1;
231
                $lastpage = 10;
232
            } else {
233
                $firstpage = $page - 4;
234
                $lastpage = $page + 5;
235
            }
236
237
            if ($firstpage < 1) {
238
                $firstpage = 1;
239
            }
240
            if ($lastpage > $numpages) {
241
                $lastpage = $numpages;
242
            }
243
244
            $numlinks = $this->generate_pagination_links($data, $URL, $firstpage, $lastpage);
245
246
            $pagelinks['nums'] = $numlinks;
247
            $pagelinks['first_result'] = $page == 1 ? 1 : ( ( $page - 1 ) * $results_per_page ) + 1;
248
            $pagelinks['last_result'] = $page == $numpages ? $total_results : $pagelinks['first_result'] + ( $results_per_page - 1 );
249
250
            if ( $page != 1 ) {
251
                $prev_page = $page - 1;
252
                $URL->insert(array( 'p' => $prev_page ) );
253
                $pagelinks['prev'] = array(
254
                    'url' => $URL->generate()
255
                );
256
                $URL->insert(array( 'p' => 1 ) );
257
                $pagelinks['firstpage'] = array(
258
                    'url' => $URL->generate()
259
                );
260
            }
261
            if ($page != $numpages) {
262
                $next_page = $page + 1;
263
                $URL->insert(array( 'p' => $next_page ) );
264
                $pagelinks['next'] = array(
265
                    'url' => $URL->generate()
266
                );
267
                $URL->insert(array( 'p' => $numpages ) );
268
                $pagelinks['lastpage'] = array(
269
                    'url' => $URL->generate()
270
                );
271
            }
272
        }
273
274
        return $pagelinks;
275
    }
276
277
    private function get_sidebar_links() {
278
        global $DATA, $SEARCHENGINE, $this_page;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
279
280
        $links = array();
281
        $links['rss'] = $DATA->page_metadata($this_page, 'rss');
282
283
        if ($SEARCHENGINE) {
284
            $links['email'] = '/alert/?' . ($this->searchstring ? 'alertsearch='.urlencode($this->searchstring) : '');
285
            $links['email_desc'] = $SEARCHENGINE->query_description_long();
286
        }
287
288
        $filter_ss = $this->searchstring;
289
        $section = get_http_var('section');
290
        if (preg_match('#\s*section:([a-z]*)#', $filter_ss, $m)) {
291
            $section = $m[1];
292
            $filter_ss = preg_replace("#\s*section:$section#", '', $filter_ss);
293
        }
294
        if ($section && $filter_ss) {
295
            $search_engine = new \SEARCHENGINE($filter_ss);
296
            $links['email_section'] = $links['email'];
297
            $links['email_desc_section'] = $links['email_desc'];
298
            $links['email'] = '/alert/?' . ($filter_ss ? 'alertsearch='.urlencode($filter_ss) : '');
299
            $links['email_desc'] = $search_engine->query_description_long();
300
        }
301
302
        return $links;
303
    }
304
}
305