Test Failed
Push — master ( 8c47c2...3acf9f )
by Steve
12:37
created

engine/lib/tags.php (2 issues)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Elgg tags
4
 * Functions for managing tags and tag clouds.
5
 *
6
 * @package Elgg.Core
7
 * @subpackage Tags
8
 */
9
10
/**
11
 * Takes in a comma-separated string and returns an array of tags
12
 * which have been trimmed
13
 *
14
 * @param string $string Comma-separated tag string
15
 *
16
 * @return mixed An array of strings or the original data if input was not a string
17
 */
18
function string_to_tag_array($string) {
19 2
	if (!is_string($string)) {
20 1
		return $string;
21
	}
22
	
23 1
	$ar = explode(",", $string);
24 1
	$ar = array_map('trim', $ar);
25 1
	$ar = array_filter($ar, 'is_not_null');
26 1
	$ar = array_map('strip_tags', $ar);
27 1
	$ar = array_unique($ar);
28 1
	return $ar;
29
}
30
31
/**
32
 * Get popular tags and their frequencies
33
 *
34
 * Supports similar arguments as elgg_get_entities()
35
 *
36
 * @param array $options Array in format:
37
 *
38
 * 	threshold => INT minimum tag count
39
 *
40
 * 	tag_names => array() metadata tag names - must be registered tags
41
 *
42
 * 	limit => INT number of tags to return (default from settings)
43
 *
44
 *  types => null|STR entity type (SQL: type = '$type')
45
 *
46
 * 	subtypes => null|STR entity subtype (SQL: subtype IN ('subtype1', 'subtype2))
47
 *              Use ELGG_ENTITIES_NO_VALUE to match the default subtype.
48
 *              Use ELGG_ENTITIES_ANY_VALUE to match any subtype.
49
 *
50
 * 	type_subtype_pairs => null|ARR (array('type' => 'subtype'))
51
 *                        array(
52
 *                            'object' => array('blog', 'file'), // All objects with subtype of 'blog' or 'file'
53
 *                            'user' => ELGG_ENTITY_ANY_VALUE, // All users irrespective of subtype
54
 *                        );
55
 *
56
 * 	owner_guids => null|INT entity guid
57
 *
58
 * 	container_guids => null|INT container_guid
59
 *
60
 * 	created_time_lower => null|INT Created time lower boundary in epoch time
61
 *
62
 * 	created_time_upper => null|INT Created time upper boundary in epoch time
63
 *
64
 * 	modified_time_lower => null|INT Modified time lower boundary in epoch time
65
 *
66
 * 	modified_time_upper => null|INT Modified time upper boundary in epoch time
67
 *
68
 * 	wheres => array() Additional where clauses to AND together
69
 *
70
 * 	joins => array() Additional joins
71
 *
72
 * @return 	object[]|false If no tags or error, false
73
 * 						   otherwise, array of objects with ->tag and ->total values
74
 * @since 1.7.1
75
 */
76
function elgg_get_tags(array $options = []) {
77
	_elgg_check_unsupported_site_guid($options);
78
	
79
	$defaults = [
80
		'threshold' => 1,
81
		'tag_names' => [],
82
		'limit' => _elgg_config()->default_limit,
83
84
		'types' => ELGG_ENTITIES_ANY_VALUE,
85
		'subtypes' => ELGG_ENTITIES_ANY_VALUE,
86
		'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
87
88
		'owner_guids' => ELGG_ENTITIES_ANY_VALUE,
89
		'container_guids' => ELGG_ENTITIES_ANY_VALUE,
90
91
		'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE,
92
		'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE,
93
		'created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
94
		'created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
95
96
		'joins' => [],
97
		'wheres' => [],
98
	];
99
100
101
	$options = array_merge($defaults, $options);
102
103
	$singulars = ['type', 'subtype', 'owner_guid', 'container_guid', 'tag_name'];
104
	$options = _elgg_normalize_plural_options_array($options, $singulars);
105
106
	$registered_tags = elgg_get_registered_tag_metadata_names();
107
108
	if (!is_array($options['tag_names'])) {
109
		return false;
110
	}
111
112
	// empty array so use all registered tag names
113
	if (count($options['tag_names']) == 0) {
114
		$options['tag_names'] = $registered_tags;
115
	}
116
117
	$wheres = $options['wheres'];
118
119
	// catch for tags that were spaces
120
	$wheres[] = "md.value != ''";
121
122
	$sanitised_tags = [];
123
	foreach ($options['tag_names'] as $tag) {
124
		$sanitised_tags[] = '"' . sanitise_string($tag) . '"';
125
	}
126
	$tags_in = implode(',', $sanitised_tags);
127
	$wheres[] = "(md.name IN ($tags_in))";
128
129
	$wheres[] = _elgg_services()->entityTable->getEntityTypeSubtypeWhereSql('e', $options['types'],
130
		$options['subtypes'], $options['type_subtype_pairs']);
131
	$wheres[] = _elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']);
132
	$wheres[] = _elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']);
133
	$wheres[] = _elgg_get_entity_time_where_sql('e', $options['created_time_upper'],
134
		$options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
135
136
	// see if any functions failed
137
	// remove empty strings on successful functions
138 View Code Duplication
	foreach ($wheres as $i => $where) {
139
		if ($where === false) {
140
			return false;
141
		} elseif (empty($where)) {
142
			unset($wheres[$i]);
143
		}
144
	}
145
146
	// remove identical where clauses
147
	$wheres = array_unique($wheres);
148
149
	$joins = $options['joins'];
150
151
	$prefix = _elgg_config()->dbprefix;
152
	$joins[] = "JOIN {$prefix}metadata md on md.entity_guid = e.guid";
153
154
	// remove identical join clauses
155
	$joins = array_unique($joins);
156
157 View Code Duplication
	foreach ($joins as $i => $join) {
158
		if ($join === false) {
159
			return false;
160
		} elseif (empty($join)) {
161
			unset($joins[$i]);
162
		}
163
	}
164
165
	$query  = "SELECT md.value as tag, count(md.id) as total ";
166
	$query .= "FROM {$prefix}entities e ";
167
168
	// add joins
169
	foreach ($joins as $j) {
170
		$query .= " $j ";
171
	}
172
173
	// add wheres
174
	$query .= ' WHERE ';
175
176
	foreach ($wheres as $w) {
177
		$query .= " $w AND ";
178
	}
179
180
	// Add access controls
181
	$query .= _elgg_get_access_where_sql();
182
183
	$threshold = sanitise_int($options['threshold']);
184
	$query .= " GROUP BY md.value HAVING total >= {$threshold} ";
185
	$query .= " ORDER BY total DESC ";
186
187
	$limit = sanitise_int($options['limit']);
188
	$query .= " LIMIT {$limit} ";
189
190
	return get_data($query);
191
}
192
193
/**
194
 * Registers a metadata name as containing tags for an entity.
195
 * This is required if you are using a non-standard metadata name
196
 * for your tags.
197
 *
198
 * @param string $name Tag name
199
 *
200
 * @return bool
201
 * @since 1.7.0
202
 */
203
function elgg_register_tag_metadata_name($name) {
0 ignored issues
show
elgg_register_tag_metadata_name uses the super-global variable $GLOBALS 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...
204
	if (!isset($GLOBALS['_ELGG']->registered_tag_metadata_names)) {
205
		$GLOBALS['_ELGG']->registered_tag_metadata_names = [];
206
	}
207
208
	if (!in_array($name, $GLOBALS['_ELGG']->registered_tag_metadata_names)) {
209
		$GLOBALS['_ELGG']->registered_tag_metadata_names[] = $name;
210
	}
211
212
	return true;
213
}
214
215
/**
216
 * Returns an array of valid metadata names for tags.
217
 *
218
 * @return array
219
 * @since 1.7.0
220
 */
221
function elgg_get_registered_tag_metadata_names() {
0 ignored issues
show
elgg_get_registered_tag_metadata_names uses the super-global variable $GLOBALS 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...
222
	$names = (isset($GLOBALS['_ELGG']->registered_tag_metadata_names)) ? $GLOBALS['_ELGG']->registered_tag_metadata_names : [];
223
224
	return $names;
225
}
226
227
/**
228
 * @access private
229
 */
230
function _elgg_tags_init() {
231
	// register the standard tags metadata name
232
	elgg_register_tag_metadata_name('tags');
233
}
234
235
return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
236
	$events->registerHandler('init', 'system', '_elgg_tags_init');
237
};
238