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

engine/classes/Elgg/EventsService.php (3 issues)

1
<?php
2
namespace Elgg;
3
4
use Elgg\HooksRegistrationService\Event;
0 ignored issues
show
This use statement conflicts with another class in this namespace, Elgg\Event. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
5
6
/**
7
 * Events service
8
 *
9
 * @tip Use ->hooks->getEvents() from the service provider.
10
 *
11
 * @access private
12
 */
13
class EventsService extends HooksRegistrationService {
14
	use Profilable;
15
16
	const OPTION_STOPPABLE = 'stoppable';
17
	const OPTION_DEPRECATION_MESSAGE = 'deprecation_message';
18
	const OPTION_DEPRECATION_VERSION = 'deprecation_version';
19
20
	/**
21
	 * @var HandlersService
22
	 */
23
	private $handlers;
24
25
	/**
26
	 * Constructor
27
	 *
28
	 * @param HandlersService $handlers Handlers
29
	 */
30 4417
	public function __construct(HandlersService $handlers) {
31 4417
		$this->handlers = $handlers;
32 4417
	}
33
34
	/**
35
	 * Get the handlers service in use
36
	 *
37
	 * @return HandlersService
38
	 */
39 1896
	public function getHandlersService() {
40 1896
		return $this->handlers;
41
	}
42
43
	/**
44
	 * {@inheritdoc}
45
	 */
46 134
	public function registerHandler($name, $type, $callback, $priority = 500) {
47 134
		if (in_array($type, ['member', 'friend', 'attached'])
48 134
				&& in_array($name, ['create', 'update', 'delete'])) {
49
			_elgg_services()->logger->error("'$name, $type' event is no longer triggered. "
50
				. "Update your event registration to use '$name, relationship'");
51
		}
52
53 134
		return parent::registerHandler($name, $type, $callback, $priority);
54
	}
55
56
	/**
57
	 * Triggers an Elgg event.
58
	 *
59
	 * @see elgg_trigger_event
60
	 * @see elgg_trigger_after_event
61
	 * @access private
62
	 */
63 2117
	public function trigger($name, $type, $object = null, array $options = []) {
64 2117
		$options = array_merge([
65 2117
			self::OPTION_STOPPABLE => true,
66 2117
			self::OPTION_DEPRECATION_MESSAGE => '',
67 2117
			self::OPTION_DEPRECATION_VERSION => '',
68 2117
		], $options);
69
70 2117
		$handlers = $this->hasHandler($name, $type);
71 2117
		if ($handlers && $options[self::OPTION_DEPRECATION_MESSAGE]) {
72
			_elgg_services()->deprecation->sendNotice(
73
				$options[self::OPTION_DEPRECATION_MESSAGE],
74
				$options[self::OPTION_DEPRECATION_VERSION],
75
				2
76
			);
77
		}
78
79 2117
		$handlers = $this->getOrderedHandlers($name, $type);
80
81
		// This starts as a string, but if a handler type-hints an object we convert it on-demand inside
82
		// \Elgg\HandlersService::call and keep it alive during all handler calls. We do this because
83
		// creating objects for every triggering is expensive.
84 2117
		$event = 'event';
85
		/* @var Event|string */
86
87 2117
		foreach ($handlers as $handler) {
88 440
			$handler_description = false;
89 440
			if ($this->timer && $type === 'system' && $name !== 'shutdown') {
90
				$handler_description = $this->handlers->describeCallable($handler) . "()";
91
				$this->timer->begin(["[$name,$type]", $handler_description]);
92
			}
93
94 440
			list($success, $return, $event) = $this->handlers->call($handler, $event, [$name, $type, $object]);
95
96 440
			if ($handler_description) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $handler_description of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
97
				$this->timer->end(["[$name,$type]", $handler_description]);
98
			}
99
100 440
			if (!$success) {
101 1
				continue;
102
			}
103
104 439
			if (!empty($options[self::OPTION_STOPPABLE]) && ($return === false)) {
105 439
				return false;
106
			}
107
		}
108
109 2114
		return true;
110
	}
111
112
	/**
113
	 * Trigger a "Before event" indicating a process is about to begin.
114
	 *
115
	 * Like regular events, a handler returning false will cancel the process and false
116
	 * will be returned.
117
	 *
118
	 * To register for a before event, append ":before" to the event name when registering.
119
	 *
120
	 * @param string $event       The event type. The fired event type will be appended with ":before".
121
	 * @param string $object_type The object type
122
	 * @param string $object      The object involved in the event
123
	 *
124
	 * @return bool False if any handler returned false, otherwise true
125
	 *
126
	 * @see trigger
127
	 * @see triggerAfter
128
	 * @since 2.0.0
129
	 */
130 1157
	function triggerBefore($event, $object_type, $object = null) {
131 1157
		return $this->trigger("$event:before", $object_type, $object);
132
	}
133
134
	/**
135
	 * Trigger an "After event" indicating a process has finished.
136
	 *
137
	 * Unlike regular events, all the handlers will be called, their return values ignored.
138
	 *
139
	 * To register for an after event, append ":after" to the event name when registering.
140
	 *
141
	 * @param string $event       The event type. The fired event type will be appended with ":after".
142
	 * @param string $object_type The object type
143
	 * @param string $object      The object involved in the event
144
	 *
145
	 * @return true
146
	 *
147
	 * @see triggerBefore
148
	 * @since 2.0.0
149
	 */
150 2100
	public function triggerAfter($event, $object_type, $object = null) {
151
		$options = [
152 2100
			self::OPTION_STOPPABLE => false,
153
		];
154 2100
		return $this->trigger("$event:after", $object_type, $object, $options);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->trigger(En...ype, $object, $options) returns the type boolean which is incompatible with the documented return type true.
Loading history...
155
	}
156
157
	/**
158
	 * Trigger an event normally, but send a notice about deprecated use if any handlers are registered.
159
	 *
160
	 * @param string $event       The event type
161
	 * @param string $object_type The object type
162
	 * @param string $object      The object involved in the event
163
	 * @param string $message     The deprecation message
164
	 * @param string $version     Human-readable *release* version: 1.9, 1.10, ...
165
	 *
166
	 * @return bool
167
	 *
168
	 * @see trigger
169
	 */
170 214
	function triggerDeprecated($event, $object_type, $object = null, $message = null, $version = null) {
171
		$options = [
172 214
			self::OPTION_DEPRECATION_MESSAGE => $message,
173 214
			self::OPTION_DEPRECATION_VERSION => $version,
174
		];
175 214
		return $this->trigger($event, $object_type, $object, $options);
176
	}
177
}
178