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

EventsService::registerHandler()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.3332

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 2
nop 4
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
ccs 4
cts 6
cp 0.6667
crap 3.3332
1
<?php
2
namespace Elgg;
3
4
use Elgg\HooksRegistrationService\Event;
0 ignored issues
show
Bug introduced by
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) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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