Passed
Push — master ( d26eb3...883d7f )
by Fabio
06:35
created

TBehaviorsModule::setAdditionalBehaviors()   B

Complexity

Conditions 9
Paths 16

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 14
c 1
b 0
f 0
nc 16
nop 1
dl 0
loc 20
rs 8.0555
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
 * @package Prado\Util
9
 */
10
11
namespace Prado\Util;
12
13
use Prado\Exceptions\TConfigurationException;
14
use Prado\Exceptions\TInvalidDataTypeException;
15
use Prado\TApplication;
16
use Prado\TComponent;
17
use Prado\Xml\TXmlDocument;
18
use Prado\Xml\TXmlElement;
19
20
/**
21
 * TBehaviorsModule class.
22
 *
23
 * TBehaviorsModule loads and attaches {@link TBehaviors}.  This attaches
24
 * Behaviors to classes and to application components like the TApplication,
25
 * individual modules, and TPage of the TPageService.
26
 *
27
 * Contents enclosed within the module tag are treated as behaviors, e.g.,
28
 * <code>
29
 * <module class="Prado\Util\TBehaviorsModule" Parameter="AdditionalBehaviors">
30
 *   <behavior Name="pagethemeparameter" Class="Prado\Util\Behaviors\TParameterizeBehavior" AttachToClass="Prade\Web\UI\TPage" Priority="10" Parameter="ThemeName" Property="Theme"/>
31
 *   <behavior Name="sharedModuleBehavior" Class="FooModuleBehavior" AttachToClass="Prado\TModule" Attribute1="abc"/>
32
 *   <behavior name="TimeZoneBehavior" Class="Prado\Util\Behaviors\TTimeZoneParameterBehavior" AttachTo="Application" Priority="10" TimeZone="America/New York" TimeZoneParameter="prop:TimeZone" />
33
 *   <behavior name="MyModuleBehavior" Class="MyModuleBehavior" AttachTo="Module:page" Property1="Value1" Property2="Value2" ... />
34
 *   <behavior name="MyPageTitleBehavior" Class="Prado\Util\Behaviors\TParameterizeBehavior" AttachTo="Page" Priority="10" Parameter="PageTitle" Property="Title" Localize="true"/>
35
 * </module>
36
 * </code>
37
 *
38
 * When the Service is not TPageService, page behaviors are not installed and have no effect other than be ignored.
39
 *
40
 * When {@link setAdditionalBehaviors AdditionalBehaviors} is set, this module
41
 * loads the behaviors from that property. It can be an array of php behavior definition arrays.
42
 * or a string that is then passed through unserialize or json_decode; otherwise is treated as
43
 * an xml document of behavior like above.
44
 *
45
 * The format is in the PHP style module configuration:
46
 * </code>
47
 *		[['name' => 'behaviorname', 'class' => 'TMyBehaviorClass', 'attachto' => 'page', 'priority' => '10', 'behaviorProperty'=>"value1'], ...]
48
 * </code>
49
 *
50
 * This allows TBehaviorsModule to load behaviors, dynamically, from parameters with the TParameterizeBehavior.
51
 *
52
 * @author Brad Anderson <[email protected]>
53
 * @package Prado\Util
54
 * @since 4.2.0
55
 */
56
class TBehaviorsModule extends \Prado\TModule
57
{
58
	/**
59
	 * @var Tbehavior[] behaviors attaching to the TPage
60
	 */
61
	private $_pageBehaviors = [];
62
	
63
	/**
64
	 * @var array[] additional behaviors in a configuration format: array[], serialized php object, json object, string of xml
65
	 */
66
	private $_additionalBehaviors;
67
	
68
	/**
69
	 * Initializes the module by loading behaviors.  If there are page behaviors, this
70
	 * attaches behaviors to TPage through TApplication::onBeginRequest and then
71
	 * TPageService::onPreRunPage.
72
	 * @param \Prado\Xml\TXmlElement $config configuration for this module, can be null
73
	 */
74
	public function init($config)
75
	{
76
		$this->loadBehaviors($config);
77
		$this->loadBehaviors(['behaviors' => $this->getAdditionalBehaviors()]);
78
		
79
		if (count($this->_pageBehaviors)) {
80
			$this->getApplication()->attachEventHandler('onBeginRequest', [$this, 'attachTPageServiceHandler']);
81
		}
82
		parent::init($config);
83
	}
84
	
85
	/**
86
	 * TApplication::onBeginRequest Handler that adds {@link attachTPageBehaviors} to
87
	 * TPageService::onPreRunPage. In turn, this attaches {@link attachTPageBehaviors}
88
	 * to TPageService to then adds the page behaviors.
89
	 * @param object $sender the object that raised the event
90
	 * @param mixed $param parameter of the event
91
	 */
92
	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

92
	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

92
	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...
93
	{
94
		$service = $this->getService();
95
		if ($service->isa('Prado\\Web\\Services\\TPageService')) {
0 ignored issues
show
Bug introduced by
The method isa() does not exist on Prado\IService. Since it exists in all sub-types, consider adding an abstract or default implementation to Prado\IService. ( Ignorable by Annotation )

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

95
		if ($service->/** @scrutinizer ignore-call */ isa('Prado\\Web\\Services\\TPageService')) {
Loading history...
96
			$service->attachEventHandler('onPreRunPage', [$this, 'attachTPageBehaviors']);
0 ignored issues
show
Bug introduced by
The method attachEventHandler() does not exist on Prado\IService. Since it exists in all sub-types, consider adding an abstract or default implementation to Prado\IService. ( Ignorable by Annotation )

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

96
			$service->/** @scrutinizer ignore-call */ 
97
             attachEventHandler('onPreRunPage', [$this, 'attachTPageBehaviors']);
Loading history...
97
		}
98
	}
99
	
100
	/**
101
	 * This method attaches page behaviors to the TPage handling the TPageService::OnPreInitPage event.
102
	 * @param object $sender the object that raised the event
103
	 * @param TPage $page the page being initialized
0 ignored issues
show
Bug introduced by
The type Prado\Util\TPage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
104
	 */
105
	public function attachTPageBehaviors($sender, $page)
106
	{
107
		foreach ($this->_pageBehaviors as $name => $properties) {
108
			$priority = $properties['priority'] ?? null;
109
			unset($properties['priority']);
110
			$page->attachBehavior($name, $properties, $priority);
111
		}
112
		$this->_pageBehaviors = [];
113
	}
114
115
	/**
116
	 * Loads behaviors and attach to the proper object. behaviors for pages are
117
	 * attached separately if and when the TPage is loaded on TPageSerivce::onPreRunPage
118
	 * @param mixed $config XML of PHP representation of the behaviors
119
	 * @throws prado\Exceptions\TConfigurationException if the parameter file format is invalid
120
	 */
121
	protected function loadBehaviors($config)
122
	{
123
		$isXml = false;
124
		if ($config instanceof TXmlElement) {
125
			$isXml = true;
126
			$config = $config->getElementsByTagName('behavior');
127
		} elseif (is_array($config)) {
128
			$config = $config['behaviors'];
129
		} elseif (!$config) {
130
			return;
131
		}
132
		foreach ($config as $properties) {
133
			if ($isXml) {
134
				$properties = array_change_key_case($properties->getAttributes()->toArray());
135
			} else {
136
				if (!is_array($properties)) {
137
					throw new TConfigurationException('behaviormodule_behavior_as_array_required');
138
				}
139
			}
140
			$name = $properties['name'];
141
			unset($properties['name']);
142
			if (!$name) {
143
				throw new TConfigurationException('behaviormodule_behaviorname_required');
144
			}
145
			
146
			$attachTo = $properties['attachto'] ?? null;
147
			$attachToClass = $properties['attachtoclass'] ?? null;
148
			unset($properties['attachto']);
149
			unset($properties['attachtoclass']);
150
			if ($attachTo === null && $attachToClass === null) {
151
				throw new TConfigurationException('behaviormodule_attachto_class_required');
152
			} elseif ($attachTo !== null && $attachToClass !== null) {
153
				throw new TConfigurationException('behaviormodule_attachto_and_class_only_one');
154
			}
155
			if ($attachToClass) {
156
				$priority = $properties['priority'] ?? null;
157
				unset($properties['priority']);
158
				TComponent::attachClassBehavior($name, $properties, $attachToClass, $priority);
0 ignored issues
show
Bug introduced by
$properties of type array is incompatible with the type object|string expected by parameter $behavior of Prado\TComponent::attachClassBehavior(). ( Ignorable by Annotation )

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

158
				TComponent::attachClassBehavior($name, /** @scrutinizer ignore-type */ $properties, $attachToClass, $priority);
Loading history...
159
			} else {
160
				if (strtolower($attachTo) == "page") {
161
					$this->_pageBehaviors[$name] = $properties;
162
					continue;
163
				} elseif (strncasecmp($attachTo, 'module:', 7) === 0) {
164
					$owner = $this->getApplication()->getModule(trim(substr($attachTo, 7)));
165
				} else {
166
					$owner = $this->getSubProperty($attachTo);
167
				}
168
				$priority = $properties['priority'] ?? null;
169
				unset($properties['priority']);
170
				if (!$owner) {
171
					throw new TConfigurationException('behaviormodule_behaviorowner_required', $attachTo);
172
				}
173
				$owner->attachBehavior($name, $properties, $priority);
174
			}
175
		}
176
	}
177
	
178
	/**
179
	 * @return array additional behaviors in a list.
180
	 */
181
	public function getAdditionalBehaviors()
182
	{
183
		return $this->_additionalBehaviors ?? [];
184
	}
185
	 
186
	/**
187
	 * this will take a string that is an array of behaviors that has been
188
	 * through serialize(), or json array of behaviors.  If one behavior is
189
	 * set as an array, then it is automatically placed into an array.
190
	 * @param array[]|string $behaviors additional behaviors
191
	 */
192
	public function setAdditionalBehaviors($behaviors)
193
	{
194
		if (is_string($behaviors)) {
195
			if (($b = @unserialize($behaviors)) !== false) {
196
				$behaviors = $b;
197
			} elseif (($b = json_decode($behaviors, true)) !== null) {
198
				$behaviors = $b;
199
			} else {
200
				$xmldoc = new TXmlDocument('1.0', 'utf-8');
201
				$xmldoc->loadFromString($behaviors);
202
				$behaviors = $xmldoc;
203
			}
204
		}
205
		if (is_array($behaviors) && isset($behaviors['class'])) {
206
			$behaviors = [$behaviors];
207
		}
208
		if (!is_array($behaviors) && !($behaviors instanceof TXmlDocument) && $behaviors !== null) {
209
			throw new TInvalidDataTypeException('behaviormodule_additional_behaviors_invalid', $behaviors);
210
		}
211
		$this->_additionalBehaviors = $behaviors ?? [];
212
	}
213
}
214