Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

engine/classes/Elgg/Database/Seeds/Seeding.php (4 issues)

1
<?php
2
/**
3
 *
4
 */
5
6
namespace Elgg\Database\Seeds;
7
8
use Elgg\Project\Paths;
9
use ElggEntity;
10
use ElggGroup;
11
use ElggObject;
12
use ElggUser;
13
use Exception;
14
use Faker\Factory;
15
16
/**
17
 * Seeding trait
18
 * Can be used to easily create new random users, groups and objects in the database
19
 *
20
 * @access private
21
 */
22
trait Seeding {
23
24
	/**
25
	 * @var int Max number of items to be created by the seed
26
	 */
27
	protected $limit = 20;
28
29
	/**
30
	 * @var \Faker\Generator
31
	 */
32
	protected $faker;
33
34
	/**
35
	 * Returns an instance of faker
36
	 *
37
	 * @param string $locale Locale
38
	 *
39
	 * @return \Faker\Generator
40
	 */
41 358
	public function faker($locale = 'en_US') {
42 358
		if (!isset($this->faker)) {
43 358
			$this->faker = Factory::create($locale);
44
		}
45
46 358
		return $this->faker;
47
	}
48
49
	/**
50
	 * Get site domain
51
	 * @return string
52
	 */
53
	public function getDomain() {
54
		return elgg_get_site_entity()->getDomain();
55
	}
56
57
	/**
58
	 * Get valid domain for emails
59
	 * @return string
60
	 */
61 160
	public function getEmailDomain() {
62 160
		$email = elgg_get_site_entity()->email;
63 160
		if (!$email) {
64
			$email = "noreply@{$this->getDomain()}";
65
		}
66
67 160
		list(, $domain) = explode('@', $email);
68
69 160
		if (sizeof(explode('.', $domain)) <= 1) {
70
			$domain = 'example.net';
71
		}
72
73 160
		return $domain;
74
	}
75
76
	/**
77
	 * Returns random unique subtype
78
	 * @return bool|string
79
	 */
80 419
	public function getRandomSubtype() {
81 419
		return substr(sha1(microtime() . rand()), 0, 25);
82
	}
83
84
	/**
85
	 * Create a new fake user
86
	 *
87
	 * @param array $attributes User entity attributes
88
	 * @param array $metadata   User entity metadata
89
	 * @param array $options    Seeding options
90
	 *
91
	 * @return ElggUser
92
	 */
93
	public function createUser(array $attributes = [], array $metadata = [], array $options = []) {
94
95 160
		$create = function () use ($attributes, $metadata, $options) {
96 160
			$metadata['__faker'] = true;
97
98 160
			if (empty($metadata['password'])) {
99 154
				$metadata['password'] = generate_random_cleartext_password();
100
			}
101
102 160
			if (empty($metadata['name'])) {
103 160
				$metadata['name'] = $this->faker()->name;
104
			}
105
106 160
			if (empty($metadata['username'])) {
107 160
				$metadata['username'] = $this->getRandomUsername($metadata['name']);
108
			}
109
110 160
			if (empty($metadata['email'])) {
111 160
				$metadata['email'] = "{$metadata['username']}@{$this->getEmailDomain()}";
112
			}
113
114 160
			if (empty($attributes['subtype'])) {
115 47
				$attributes['subtype'] = 'user';
116
			}
117
118 160
			$user = false;
119
120
			try {
121 160
				$guid = register_user($metadata['username'], $metadata['password'], $metadata['name'], $metadata['email'], false, $attributes['subtype']);
0 ignored issues
show
It seems like $metadata['password'] can also be of type true; however, parameter $password of register_user() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

121
				$guid = register_user($metadata['username'], /** @scrutinizer ignore-type */ $metadata['password'], $metadata['name'], $metadata['email'], false, $attributes['subtype']);
Loading history...
It seems like $metadata['name'] can also be of type true; however, parameter $name of register_user() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

121
				$guid = register_user($metadata['username'], $metadata['password'], /** @scrutinizer ignore-type */ $metadata['name'], $metadata['email'], false, $attributes['subtype']);
Loading history...
It seems like $metadata['username'] can also be of type true; however, parameter $username of register_user() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

121
				$guid = register_user(/** @scrutinizer ignore-type */ $metadata['username'], $metadata['password'], $metadata['name'], $metadata['email'], false, $attributes['subtype']);
Loading history...
It seems like $metadata['email'] can also be of type true; however, parameter $email of register_user() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

121
				$guid = register_user($metadata['username'], $metadata['password'], $metadata['name'], /** @scrutinizer ignore-type */ $metadata['email'], false, $attributes['subtype']);
Loading history...
122
123 160
				$user = get_user($guid);
124 160
				if (!$user) {
125
					throw new Exception("Unable to create new user with attributes: " . print_r($attributes, true));
126
				}
127
128 160
				if (isset($metadata['admin'])) {
129 1
					if ($metadata['admin']) {
130 1
						$user->makeAdmin();
131
					} else {
132
						$user->removeAdmin();
133
					}
134
				}
135
136 160
				if (isset($metadata['banned'])) {
137 2
					if ($metadata['banned']) {
138 2
						$user->ban('Banned by seeder');
139
					} else {
140
						$user->unban('Unbanned by seeder');
141
					}
142
				}
143
144 160
				unset($metadata['username']);
145 160
				unset($metadata['password']);
146 160
				unset($metadata['name']);
147 160
				unset($metadata['email']);
148 160
				unset($metadata['banned']);
149 160
				unset($metadata['admin']);
150
151 160
				$user->setValidationStatus($this->faker()->boolean(), 'seeder');
152
153 160
				$user->setNotificationSetting('email', false);
154 160
				$user->setNotificationSetting('site', true);
155
156 160
				$profile_fields = elgg_extract('profile_fields', $options, []);
157 160
				$user = $this->populateMetadata($user, $profile_fields, $metadata);
158
159 160
				$user->save();
160
161 160
				$this->log("Created new user $user->name [guid: $user->guid]");
162
163 160
				return $user;
164
			} catch (\RegistrationException $e) {
165
				if ($user && $user->guid) {
166
					$user->delete();
167
				}
168
169
				$attr_log = print_r($attributes, true);
170
				$this->log("User creation failed with message {$e->getMessage()} [attributes: $attr_log]");
171
172
				return false;
173
			}
174 160
		};
175
176 160
		$ia = elgg_set_ignore_access(true);
177
178 160
		$user = false;
179 160
		while (!$user instanceof \ElggUser) {
180 160
			$user = $create();
181
		}
182
183 160
		elgg_set_ignore_access($ia);
184
185 160
		return $user;
186
187
	}
188
189
	/**
190
	 * Create a new fake group
191
	 *
192
	 * @param array $attributes Group entity attributes
193
	 * @param array $metadata   Group entity metadata
194
	 * @param array $options    Additional options
195
	 *
196
	 * @return ElggGroup
197
	 */
198
	public function createGroup(array $attributes = [], array $metadata = [], array $options = []) {
199
200 73
		$create = function () use ($attributes, $metadata, $options) {
201 73
			$metadata['__faker'] = true;
202
203 73
			if (empty($attributes['access_id'])) {
204 73
				$attributes['access_id'] = ACCESS_PUBLIC;
205
			}
206
207 73
			if (empty($metadata['content_access_mode'])) {
208 73
				$metadata['content_access_mode'] = ElggGroup::CONTENT_ACCESS_MODE_UNRESTRICTED;
209
			}
210
211 73
			if (empty($metadata['membership'])) {
212 73
				$metadata['membership'] = ACCESS_PUBLIC;
213
			}
214
215 73
			if (empty($metadata['name'])) {
216 73
				$metadata['name'] = $this->faker()->sentence();
217
			}
218
219 73
			if (empty($metadata['description'])) {
220 73
				$metadata['description'] = $this->faker()->text($this->faker()->numberBetween(500, 1000));
221
			}
222
223 73
			if (empty($attributes['owner_guid'])) {
224 73
				$user = elgg_get_logged_in_user_entity();
225 73
				if (!$user) {
226 66
					$user = $this->getRandomUser();
227
				}
228 73
				if (!$user) {
229
					$user = $this->createUser();
230
				}
231
232 73
				$attributes['owner_guid'] = $user->guid;
233
			}
234
235 73
			if (empty($attributes['container_guid'])) {
236 73
				$attributes['container_guid'] = $attributes['owner_guid'];
237
			}
238
239 73
			if (empty($attributes['subtype'])) {
240 6
				$attributes['subtype'] = 'group';
241
			}
242
243 73
			$owner = get_entity($attributes['owner_guid']);
244 73
			if (!$owner) {
245
				return false;
246
			}
247
248 73
			$container = get_entity($attributes['container_guid']);
249 73
			if (!$container) {
250
				return false;
251
			}
252
253 73
			$tool_options = elgg_extract('group_tools_options', $options, []);
254 73
			if ($tool_options) {
255
				foreach ($tool_options as $group_option) {
256
					$option_toggle_name = $group_option->name . "_enable";
257
					$option_default = $group_option->default_on ? 'yes' : 'no';
258
					$metadata[$option_toggle_name] = $option_default;
259
				}
260
			}
261
262 73
			if ($this->faker()->boolean(20)) {
263 11
				$metadata['featured_group'] = 'yes';
264
			}
265
266 73
			$group = new ElggGroup();
267 73
			foreach ($attributes as $name => $value) {
268 73
				$group->$name = $value;
269
			}
270
271 73
			$profile_fields = elgg_extract('profile_fields', $options, []);
272 73
			$group = $this->populateMetadata($group, $profile_fields, $metadata);
273
274 73
			$group->save();
275
276 73
			if ($group->access_id == ACCESS_PRIVATE) {
277
				$acls = $group->getOwnedAccessCollections(['subtype' => 'group_acl']);
278
				if ($acls) {
279
					$group->access_id = $acls[0]->id;
280
					$group->save();
281
				}
282
			}
283
284 73
			$group->join(get_entity($attributes['owner_guid']));
285
286 73
			elgg_create_river_item([
287 73
				'view' => 'river/group/create',
288 73
				'action_type' => 'create',
289 73
				'subject_guid' => $owner->guid,
290 73
				'object_guid' => $group->guid,
291 73
				'target_guid' => $container->guid,
292
			]);
293
294 73
			$this->log("Created new group $group->name [guid: $group->guid]");
295
296 73
			return $group;
297 73
		};
298
299 73
		$ia = elgg_set_ignore_access(true);
300
301 73
		$group = false;
302 73
		while (!$group instanceof \ElggGroup) {
303 73
			$group = $create();
304
		}
305
306 73
		elgg_set_ignore_access($ia);
307
308 73
		return $group;
309
	}
310
311
	/**
312
	 * Create a new fake object
313
	 *
314
	 * @param array $attributes Object entity attributes
315
	 * @param array $metadata   Object entity metadata
316
	 * @param array $options    Additional options
317
	 *
318
	 * @return ElggObject
319
	 */
320
	public function createObject(array $attributes = [], array $metadata = [], array $options = []) {
321
322 180
		$create = function () use ($attributes, $metadata, $options) {
323
324 180
			$properties = array_merge($metadata, $attributes);
325
326 180
			$properties['__faker'] = true;
327
328 180
			if (empty($properties['title'])) {
329 179
				$properties['title'] = $this->faker()->sentence();
330
			}
331
332 180
			if (empty($properties['description'])) {
333 179
				$properties['description'] = $this->faker()->text($this->faker()->numberBetween(500, 1000));
334
			}
335
336 180
			if (empty($properties['subtype'])) {
337 36
				$properties['subtype'] = $this->getRandomSubtype();
338
			}
339
340 180
			if (empty($properties['tags'])) {
341 158
				$properties['tags'] = $this->faker()->words(10);
342
			}
343
344 180
			if (empty($properties['container_guid'])) {
345 179
				$container = elgg_get_logged_in_user_entity();
346 179
				if (!$container) {
347 111
					$container = $this->getRandomUser();
348
				}
349 179
				if (!$container) {
350
					$container = $this->createUser();
351
				}
352
353 179
				$properties['container_guid'] = $container->guid;
354
			}
355
356 180
			$container = get_entity($properties['container_guid']);
357 180
			if (!$container) {
358
				return false;
359
			}
360
361 180
			if (empty($properties['owner_guid'])) {
362 166
				$owner = $container;
363 166
				$properties['owner_guid'] = $owner->guid;
364
			}
365
366 180
			$owner = get_entity($properties['owner_guid']);
367 180
			if (!$owner) {
368
				return false;
369
			}
370
371 180
			if (!isset($properties['access_id'])) {
372 175
				$properties['access_id'] = ACCESS_PUBLIC;
373
			}
374
375 180
			$class = elgg_get_entity_class('object', $properties['subtype']);
376 180
			if ($class && class_exists($class)) {
377 26
				$object = new $class();
378
			} else {
379 155
				$object = new ElggObject();
380
			}
381
382 180
			foreach ($properties as $name => $value) {
383 180
				$object->$name = $value;
384
			}
385
386 180
			$profile_fields = elgg_extract('profile_fields', $options, []);
387 180
			$object = $this->populateMetadata($object, $profile_fields, $properties);
388
389 180
			if (elgg_extract('save', $options, true)) {
390 180
				$object->save();
391
			}
392
393 180
			$type_str = elgg_echo("item:object:{$object->getSubtype()}");
394
395 180
			$this->log("Created new item in $type_str $object->title [guid: $object->guid]");
396
397 180
			return $object;
398 180
		};
399
400 180
		$ia = elgg_set_ignore_access(true);
401
402 180
		$object = false;
403 180
		while (!$object instanceof \ElggObject) {
404 180
			$object = $create();
405
		}
406
407 180
		elgg_set_ignore_access($ia);
408
409 180
		return $object;
410
411
	}
412
413
	/**
414
	 * Create a new fake site
415
	 *
416
	 * @param array $attributes Object entity attributes
417
	 * @param array $metadata   Object entity metadata
418
	 *
419
	 * @return ElggObject
420
	 */
421
	public function createSite(array $attributes = [], array $metadata = []) {
422
		// We don't want to create more than one site
423
		return elgg_get_site_entity();
424
	}
425
426
	/**
427
	 * Returns random fake user
428
	 *
429
	 * @param int[] $exclude GUIDs to exclude
430
	 *
431
	 * @return ElggUser|false
432
	 */
433 178
	public function getRandomUser(array $exclude = []) {
434
435 178
		$exclude[] = 0;
436 178
		$exclude_in = implode(',', array_map(function ($e) {
437 178
			return (int) $e;
438 178
		}, $exclude));
439
440 178
		$users = elgg_get_entities([
441 178
			'types' => 'user',
442
			'metadata_names' => ['__faker'],
443 178
			'limit' => 1,
444
			'wheres' => [
445 178
				"e.guid NOT IN ($exclude_in)",
446
			],
447 178
			'order_by' => 'RAND()',
448
		]);
449
450 178
		return $users ? $users[0] : false;
451
	}
452
453
	/**
454
	 * Returns random fake group
455
	 *
456
	 * @param int[] $exclude GUIDs to exclude
457
	 *
458
	 * @return ElggGroup|false
459
	 */
460
	public function getRandomGroup(array $exclude = []) {
461
462
		$exclude[] = 0;
463
		$exclude_in = implode(',', array_map(function ($e) {
464
			return (int) $e;
465
		}, $exclude));
466
467
		$groups = elgg_get_entities([
468
			'types' => 'group',
469
			'metadata_names' => ['__faker'],
470
			'limit' => 1,
471
			'wheres' => [
472
				"e.guid NOT IN ($exclude_in)",
473
			],
474
			'order_by' => 'RAND()',
475
		]);
476
477
		return $groups ? $groups[0] : false;
478
	}
479
480
	/**
481
	 * Get random access id
482
	 *
483
	 * @param ElggUser   $user      User
484
	 * @param ElggEntity $container Container
485
	 *
486
	 * @return int
487
	 */
488
	public function getRandomAccessId(\ElggUser $user = null, ElggEntity $container = null) {
489
490
		$params = [
491
			'container_guid' => $container ? $container->guid : null,
492
		];
493
494
		$access_array = get_write_access_array($user->guid, null, null, $params);
495
496
		$access_key = array_rand($access_array, 1);
497
498
		return $access_array[$access_key];
499
	}
500
501
	/**
502
	 * Generates a unique available and valid username
503
	 *
504
	 * @param string $base_name Display name, email or other prefix to use as basis
505
	 *
506
	 * @return string
507
	 */
508 172
	public function getRandomUsername($base_name = 'user') {
509
510 172
		$available = false;
511
512 172
		$base_name = iconv('UTF-8', 'ASCII//TRANSLIT', $base_name);
513 172
		$blacklist = '/[\x{0080}-\x{009f}\x{00a0}\x{2000}-\x{200f}\x{2028}-\x{202f}\x{3000}\x{e000}-\x{f8ff}]/u';
514
		$blacklist2 = [
515 172
			' ',
516
			'\'',
517
			'/',
518
			'\\',
519
			'"',
520
			'*',
521
			'&',
522
			'?',
523
			'#',
524
			'%',
525
			'^',
526
			'(',
527
			')',
528
			'{',
529
			'}',
530
			'[',
531
			']',
532
			'~',
533
			'?',
534
			'<',
535
			'>',
536
			';',
537
			'|',
538
			'¬',
539
			'`',
540
			'@',
541
			'-',
542
			'+',
543
			'='
544
		];
545
546 172
		$base_name = preg_replace($blacklist, '', $base_name);
547 172
		$base_name = str_replace($blacklist2, '', $base_name);
548 172
		$base_name = str_replace('.', '_', $base_name);
549
550 172
		$ia = elgg_set_ignore_access(true);
551
552 172
		$ha = access_get_show_hidden_status();
553 172
		access_show_hidden_entities(true);
554
555 172
		$minlength = elgg_get_config('minusername') ? : 8;
556 172
		if ($base_name) {
557 172
			$fill = $minlength - strlen($base_name);
558
		} else {
559
			$fill = 8;
560
		}
561
562 172
		$separator = '';
563
564 172
		if ($fill > 0) {
565 26
			$suffix = (new \ElggCrypto())->getRandomString($fill);
566 26
			$base_name = "$base_name$separator$suffix";
567
		}
568
569 172
		$iterator = 0;
570 172
		while (!$available) {
571 172
			if ($iterator > 0) {
572
				$base_name = "$base_name$separator$iterator";
573
			}
574 172
			$user = get_user_by_username($base_name);
575 172
			$available = !$user;
576
			try {
577 172
				if ($available) {
578 172
					validate_username($base_name);
579
				}
580
			} catch (\Exception $e) {
581
				if ($iterator >= 10) {
582
					// too many failed attempts
583
					$base_name = (new \ElggCrypto())->getRandomString(8);
584
				}
585
			}
586
587 172
			$iterator++;
588
		}
589
590 172
		access_show_hidden_entities($ha);
591 172
		elgg_set_ignore_access($ia);
592
593 172
		return strtolower($base_name);
594
	}
595
596
	/**
597
	 * Set random metadata
598
	 *
599
	 * @param ElggEntity $entity   Entity
600
	 * @param array      $fields   An array of profile fields in $name => $input_type format
601
	 * @param array      $metadata Other metadata $name => $value pairs to set
602
	 *
603
	 * @return ElggEntity
604
	 */
605 358
	public function populateMetadata(ElggEntity $entity, array $fields = [], array $metadata = []) {
606
607 358
		foreach ($fields as $name => $type) {
608
			if (isset($metadata[$name])) {
609
				continue;
610
			}
611
612
			switch ($name) {
613
				case 'phone' :
614
				case 'mobile' :
615
					$metadata[$name] = $this->faker()->phoneNumber;
616
					break;
617
618
				default :
619
					switch ($type) {
620
						case 'plaintext' :
621
						case 'longtext' :
622
							$metadata[$name] = $this->faker()->text($this->faker()->numberBetween(500, 1000));
623
							break;
624
625
						case 'text' :
626
							$metadata[$name] = $this->faker()->sentence;
627
							break;
628
629
						case 'tags' :
630
							$metadata[$name] = $this->faker()->words(10);
631
							break;
632
633
						case 'url' :
634
							$metadata[$name] = $this->faker()->url;
635
							break;
636
637
						case 'email' :
638
							$metadata[$name] = $this->faker()->email;
639
							break;
640
641
						case 'number' :
642
							$metadata[$name] = $this->faker()->randomNumber();
643
							break;
644
645
						case 'date' :
646
							$metadata[$name] = $this->faker()->unixTime;
647
							break;
648
649
						case 'password' :
650
							$metadata[$name] = generate_random_cleartext_password();
651
							break;
652
653
						case 'location' :
654
							$metadata[$name] = $this->faker()->address;
655
							$metadata['geo:lat'] = $this->faker()->latitude;
656
							$metadata['geo:long'] = $this->faker()->longitude;
657
							break;
658
659
						default :
660
							$metadata[$name] = '';
661
							break;
662
					}
663
664
					break;
665
			}
666
		}
667
668 358
		foreach ($metadata as $key => $value) {
669 358
			if (array_key_exists($key, $fields) && $entity instanceof ElggUser) {
670
				$entity->deleteAnnotations("profile:$key");
671
				$value = (array) $value;
672
				foreach ($value as $val) {
673
					$entity->annotate("profile:$key", $val, $this->getRandomAccessId($entity), $entity->guid);
674
				}
675
			} else {
676 358
				$entity->$key = $value;
677
			}
678
		}
679
680 358
		return $entity;
681
	}
682
683
	/**
684
	 * Create an icon for an entity
685
	 *
686
	 * @param ElggEntity $entity Entity
687
	 *
688
	 * @return bool
689
	 */
690
	public function createIcon(ElggEntity $entity) {
691
692
		$icon_location = $this->faker()->image();
693
		if (empty($icon_location)) {
694
			return false;
695
		}
696
697
		$result = $entity->saveIconFromLocalFile($icon_location);
698
699
		if ($result && $entity instanceof ElggUser) {
700
			elgg_create_river_item([
701
				'view' => 'river/user/default/profileiconupdate',
702
				'action_type' => 'update',
703
				'subject_guid' => $entity->guid,
704
				'object_guid' => $entity->guid,
705
			]);
706
		}
707
708
		return $result;
709
	}
710
711
	/**
712
	 * Create comments/replies
713
	 *
714
	 * @param ElggEntity $entity Entity to comment on
715
	 * @param int        $limit  Number of comments to create
716
	 *
717
	 * @return int Number of generated comments
718
	 */
719
	public function createComments(ElggEntity $entity, $limit = null) {
720
721
		$ia = elgg_set_ignore_access(true);
722
723
		$tries = 0;
724
		$success = 0;
725
726
		if (!$limit) {
727
			$limit = $this->faker()->numberBetween(1, 20);
728
		}
729
730
		while ($tries < $limit) {
731
			$comment = new \ElggComment();
732
			$comment->subtype = $entity->getSubtype() == 'discussion' ? 'discussion_reply' : 'comment';
733
			$comment->owner_guid = $this->getRandomUser()->guid ? : $entity->owner_guid;
734
			$comment->container_guid = $entity->guid;
735
			$comment->description = $this->faker()->paragraph;
736
737
			$tries++;
738
			if ($comment->save()) {
739
				$success++;
740
			}
741
		}
742
743
		elgg_set_ignore_access($ia);
744
745
		return $success;
746
747
	}
748
749
	/**
750
	 * Create likes
751
	 *
752
	 * @param ElggEntity $entity Entity to like
753
	 * @param int        $limit  Number of likes to create
754
	 *
755
	 * @return int
756
	 */
757
	public function createLikes(ElggEntity $entity, $limit = null) {
758
759
		$ia = elgg_set_ignore_access(true);
760
761
		$success = 0;
762
763
		if (!$limit) {
764
			$limit = $this->faker()->numberBetween(1, 20);
765
		}
766
767
		while ($success < $limit) {
768
			if ($entity->annotate('likes', true, $entity->access_id, $this->getRandomUser()->guid)) {
769
				$success++;
770
			}
771
		}
772
773
		elgg_set_ignore_access($ia);
774
775
		return $success;
776
	}
777
778
	/**
779
	 * Log a message
780
	 *
781
	 * @param string $msg   Message to log
782
	 * @param string $level Message level
783
	 *                      Note that 'ERROR' will terminate further code execution
784
	 *
785
	 * @return void
786
	 */
787 358
	public function log($msg, $level = 'NOTICE') {
788 358
		elgg_log($msg, $level);
789 358
	}
790
791
}