Passed
Push — master ( cfbd49...23bc0e )
by Fabio
15:35 queued 09:33
created

TBehaviorsModule::loadBehaviors()   F

Complexity

Conditions 20
Paths 223

Size

Total Lines 74
Code Lines 62

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 20
eloc 62
c 1
b 0
f 0
nc 223
nop 1
dl 0
loc 74
rs 3.0458

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * TBehaviorsModule class
4
 *
5
 * @author Brad Anderson <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 */
9
10
namespace Prado\Util;
11
12
use Prado\Exceptions\TConfigurationException;
13
use Prado\Exceptions\TInvalidDataTypeException;
14
use Prado\TApplication;
15
use Prado\TComponent;
16
use Prado\Xml\TXmlDocument;
17
use Prado\Xml\TXmlElement;
18
19
/**
20
 * TBehaviorsModule class.
21
 *
22
 * TBehaviorsModule loads and attaches {@link TBehavior}.  This attaches
23
 * Behaviors to classes and to application components like the TApplication,
24
 * individual modules, and TPage of the TPageService.
25
 *
26
 * Contents enclosed within the module tag are treated as behaviors, e.g.,
27
 * <code>
28
 * <module class="Prado\Util\TBehaviorsModule" Parameter="AdditionalBehaviors">
29
 *   <behavior Name="pagethemeparameter" Class="Prado\Util\Behaviors\TParameterizeBehavior" AttachToClass="Prade\Web\UI\TPage" Priority="10" Parameter="ThemeName" Property="Theme"/>
30
 *   <behavior Name="sharedModuleBehavior" Class="FooModuleBehavior" AttachToClass="Prado\TModule" Attribute1="abc"/>
31
 *   <behavior name="TimeZoneBehavior" Class="Prado\Util\Behaviors\TTimeZoneParameterBehavior" AttachTo="Application" Priority="10" TimeZone="America/New York" TimeZoneParameter="prop:TimeZone" />
32
 *   <behavior name="MyModuleBehavior" Class="MyModuleBehavior" AttachTo="Module:page" Property1="Value1" Property2="Value2" ... />
33
 *   <behavior name="MyPageTitleBehavior" Class="Prado\Util\Behaviors\TParameterizeBehavior" AttachTo="Page" Priority="10" Parameter="PageTitle" Property="Title" Localize="true"/>
34
 * </module>
35
 * </code>
36
 *
37
 * When the Service is not TPageService, page behaviors are not installed and have no effect other than be ignored.
38
 *
39
 * When {@link setAdditionalBehaviors AdditionalBehaviors} is set, this module
40
 * loads the behaviors from that property. It can be an array of php behavior definition arrays.
41
 * or a string that is then passed through unserialize or json_decode; otherwise is treated as
42
 * an xml document of behavior like above.
43
 *
44
 * The format is in the PHP style module configuration:
45
 * </code>
46
 *		[['name' => 'behaviorname', 'class' => 'TMyBehaviorClass', 'attachto' => 'page', 'priority' => '10', 'behaviorProperty'=>"value1'], ...]
47
 * </code>
48
 *
49
 * This allows TBehaviorsModule to load behaviors, dynamically, from parameters with the TParameterizeBehavior.
50
 *
51
 * Multiple TBehaviorsModules can be instanced, for instance, a second set of behaviors
52
 * could be configured in TPageService.  The second TBehaviorsModule can reference
53
 * behaviors from the first to attach its behaviors.
54
 *
55
 * @author Brad Anderson <[email protected]>
56
 * @since 4.2.0
57
 */
58
class TBehaviorsModule extends \Prado\TModule
59
{
60
	/**
61
	 * @var IBaseBehavior[] loaded behaviors.
62
	 */
63
	private static $_behaviors = [];
64
65
	/**
66
	 * @var IBaseBehavior[] behaviors attaching to the TPage
67
	 */
68
	private $_pageBehaviors = [];
69
70
	/**
71
	 * @var array[] delayed behaviors attaching to the loaded behaviors
72
	 */
73
	private $_behaviorBehaviors = [];
74
75
	/**
76
	 * @var array[] additional behaviors in a configuration format: array[], serialized php object, json object, string of xml
77
	 */
78
	private $_additionalBehaviors;
79
80
	/**
81
	 * Constructor.
82
	 */
83
	public function __construct()
84
	{
85
		parent::__construct();
86
	}
87
88
	/**
89
	 * Initializes the module by loading behaviors.  If there are page behaviors, this
90
	 * attaches behaviors to TPage through TApplication::onBeginRequest and then
91
	 * TPageService::onPreRunPage.
92
	 * @param \Prado\Xml\TXmlElement $config configuration for this module, can be null
93
	 */
94
	public function init($config)
95
	{
96
		$this->loadBehaviors($config);
97
		$this->loadBehaviors(['behaviors' => $this->getAdditionalBehaviors()]);
98
99
		if (count($this->_pageBehaviors)) {
100
			$this->getApplication()->attachEventHandler('onBeginRequest', [$this, 'attachTPageServiceHandler']);
101
		}
102
		foreach ($this->_behaviorBehaviors as $target => $behaviors) {
103
			if (!isset(self::$_behaviors[$target])) {
104
				throw new TConfigurationException('behaviormodule_behaviorowner_required', 'behavior:' . $target);
105
			}
106
			$owner = self::$_behaviors[$target];
107
			$owner = is_a($owner, '\WeakReference') ? $owner->get() : $owner;
0 ignored issues
show
Bug introduced by
The method get() does not exist on Prado\Util\IBaseBehavior. It seems like you code against a sub-type of said class. However, the method does not exist in Prado\Util\IBehavior or Prado\Util\IClassBehavior. Are you sure you never get one of those? ( Ignorable by Annotation )

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

107
			$owner = is_a($owner, '\WeakReference') ? $owner->/** @scrutinizer ignore-call */ get() : $owner;
Loading history...
108
			foreach ($behaviors as $properties) {
109
				$priority = $properties['priority'] ?? null;
110
				unset($properties['priority']);
111
				$name = $properties['name'];
112
				unset($properties['name']);
113
				$owner->attachBehavior($name, $properties, $priority);
0 ignored issues
show
Bug introduced by
The method attachBehavior() does not exist on Prado\Util\IBaseBehavior. Did you maybe mean attach()? ( Ignorable by Annotation )

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

113
				$owner->/** @scrutinizer ignore-call */ 
114
            attachBehavior($name, $properties, $priority);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
114
			}
115
		}
116
		$this->_behaviorBehaviors = [];
117
		parent::init($config);
118
	}
119
120
	/**
121
	 * TApplication::onBeginRequest Handler that adds {@link attachTPageBehaviors} to
122
	 * TPageService::onPreRunPage. In turn, this attaches {@link attachTPageBehaviors}
123
	 * to TPageService to then adds the page behaviors.
124
	 * @param object $sender the object that raised the event
125
	 * @param mixed $param parameter of the event
126
	 */
127
	public function attachTPageServiceHandler($sender, $param)
0 ignored issues
show
Unused Code introduced by
The parameter $sender is not used and could be removed. ( Ignorable by Annotation )

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

127
	public function attachTPageServiceHandler(/** @scrutinizer ignore-unused */ $sender, $param)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $param is not used and could be removed. ( Ignorable by Annotation )

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

127
	public function attachTPageServiceHandler($sender, /** @scrutinizer ignore-unused */ $param)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
128
	{
129
		$service = $this->getService();
130
		if ($service instanceof \Prado\Web\Services\TPageService) {
131
			$service->attachEventHandler('onPreRunPage', [$this, 'attachTPageBehaviors']);
132
		}
133
	}
134
135
	/**
136
	 * This method attaches page behaviors to the TPage handling the TPageService::OnPreInitPage event.
137
	 * @param object $sender the object that raised the event
138
	 * @param \Prado\Web\UI\TPage $page the page being initialized
139
	 */
140
	public function attachTPageBehaviors($sender, $page)
141
	{
142
		foreach ($this->_pageBehaviors as $name => $properties) {
143
			$priority = $properties['priority'] ?? null;
144
			unset($properties['priority']);
145
			$page->attachBehavior($name, $properties, $priority);
146
		}
147
		$this->_pageBehaviors = [];
148
	}
149
150
	/**
151
	 * Loads behaviors and attach to the proper object. behaviors for pages are
152
	 * attached separately if and when the TPage is loaded on TPageSerivce::onPreRunPage
153
	 * @param mixed $config XML of PHP representation of the behaviors
154
	 * @throws \Prado\Exceptions\TConfigurationException if the parameter file format is invalid
155
	 */
156
	protected function loadBehaviors($config)
157
	{
158
		$isXml = false;
159
		if ($config instanceof TXmlElement) {
160
			$isXml = true;
161
			$config = $config->getElementsByTagName('behavior');
162
		} elseif (is_array($config)) {
163
			$config = $config['behaviors'];
164
		} elseif (!$config) {
165
			return;
166
		}
167
		foreach ($config as $properties) {
168
			$element = $properties;
169
			if ($isXml) {
170
				$properties = array_change_key_case($properties->getAttributes()->toArray());
171
			} else {
172
				if (!is_array($properties)) {
173
					throw new TConfigurationException('behaviormodule_behavior_as_array_required');
174
				}
175
				if (isset($properties['properties'])) {
176
					$properties = $properties['properties'];
177
					unset($element['properties']);
178
					$properties['class'] = $element['class'];
179
					unset($element['class']);
180
				} else {
181
					unset($element['name']);
182
					unset($element['class']);
183
					unset($element['attachto']);
184
					unset($element['attachtoclass']);
185
					unset($element['priority']);
186
				}
187
			}
188
			$properties[IBaseBehavior::CONFIG_KEY] = $element;
189
			$name = $properties['name'] ?? null;
190
			unset($properties['name']);
191
			if (empty($name) || is_numeric($name)) {
192
				$name = null;
193
			}
194
195
			$attachTo = $properties['attachto'] ?? null;
196
			$attachToClass = $properties['attachtoclass'] ?? null;
197
			unset($properties['attachto']);
198
			unset($properties['attachtoclass']);
199
			if ($attachTo === null && $attachToClass === null) {
200
				throw new TConfigurationException('behaviormodule_attachto_class_required');
201
			} elseif ($attachTo !== null && $attachToClass !== null) {
202
				throw new TConfigurationException('behaviormodule_attachto_and_class_only_one');
203
			}
204
			if ($attachToClass) {
205
				$priority = $properties['priority'] ?? null;
206
				unset($properties['priority']);
207
				$behavior = TComponent::attachClassBehavior($name, $properties, $attachToClass, $priority);
208
			} else {
209
				if (strtolower($attachTo) == "page") {
210
					$this->_pageBehaviors[$name] = $properties;
211
					continue;
212
				} elseif (strncasecmp($attachTo, 'module:', 7) === 0) {
213
					$owner = $this->getApplication()->getModule(trim(substr($attachTo, 7)));
214
				} elseif (strncasecmp($attachTo, 'behavior:', 9) === 0) {
215
					$properties['name'] = $name;
216
					$this->_behaviorBehaviors[trim(substr($attachTo, 9))][] = $properties;
217
					continue;
218
				} else {
219
					$owner = $this->getSubProperty($attachTo);
220
				}
221
				$priority = $properties['priority'] ?? null;
222
				unset($properties['priority']);
223
				if (!$owner) {
224
					throw new TConfigurationException('behaviormodule_behaviorowner_required', $attachTo);
225
				}
226
				$behavior = $owner->attachBehavior($name, $properties, $priority);
227
			}
228
			if (!is_array($behavior)) {
229
				self::$_behaviors[$name] = \WeakReference::create($behavior);
230
			}
231
		}
232
	}
233
234
	/**
235
	 * @return array additional behaviors in a list.
236
	 */
237
	public function getAdditionalBehaviors()
238
	{
239
		return $this->_additionalBehaviors ?? [];
240
	}
241
242
	/**
243
	 * this will take a string that is an array of behaviors that has been
244
	 * through serialize(), or json array of behaviors.  If one behavior is
245
	 * set as an array, then it is automatically placed into an array.
246
	 * @param array[]|string $behaviors additional behaviors
247
	 */
248
	public function setAdditionalBehaviors($behaviors)
249
	{
250
		if (is_string($behaviors)) {
251
			if (($b = @unserialize($behaviors)) !== false) {
252
				$behaviors = $b;
253
			} elseif (($b = json_decode($behaviors, true)) !== null) {
254
				$behaviors = $b;
255
			} else {
256
				$xmldoc = new TXmlDocument('1.0', 'utf-8');
257
				$xmldoc->loadFromString($behaviors);
258
				$behaviors = $xmldoc;
259
			}
260
		}
261
		if (is_array($behaviors) && isset($behaviors['class'])) {
262
			$behaviors = [$behaviors];
263
		}
264
		if (!is_array($behaviors) && !($behaviors instanceof TXmlDocument) && $behaviors !== null) {
265
			throw new TInvalidDataTypeException('behaviormodule_additional_behaviors_invalid', $behaviors);
266
		}
267
		$this->_additionalBehaviors = $behaviors ?? [];
268
	}
269
}
270