Test Failed
Branch master (206474)
by Fabio
18:24
created

TPage   F

Complexity

Total Complexity 167

Size/Duplication

Total Lines 1205
Duplicated Lines 2.49 %

Coupling/Cohesion

Components 3
Dependencies 23

Importance

Changes 0
Metric Value
dl 30
loc 1205
rs 0.6197
c 0
b 0
f 0
wmc 167
lcom 3
cbo 23

76 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A run() 0 19 3
B processNormalRequest() 0 33 1
B processPostBackRequest() 0 44 1
A decodeUTF8() 0 13 4
B processCallbackRequest() 0 71 5
A getCallbackClient() 0 8 2
A setCallbackClient() 0 4 1
A getCallbackEventTarget() 0 4 1
A setCallbackEventTarget() 0 4 1
A getCallbackEventParameter() 0 4 1
A setCallbackEventParameter() 0 4 1
A getForm() 0 4 1
A setForm() 0 8 2
B getValidators() 0 17 5
B validate() 0 18 7
B getIsValid() 0 15 6
A getTheme() 7 7 2
A setTheme() 0 4 2
A getStyleSheetTheme() 7 7 2
A setStyleSheetTheme() 0 4 2
A applyControlSkin() 0 6 2
A applyControlStyleSheet() 0 6 2
A getClientScript() 0 13 4
A onPreInit() 0 4 1
A onInitComplete() 0 4 1
A onPreLoad() 0 4 1
A onLoadComplete() 0 4 1
D onPreRenderComplete() 16 27 9
A getCssMediaType() 0 9 2
A onSaveStateComplete() 0 4 1
A determinePostBackMode() 0 7 3
A getIsPostBack() 0 4 1
A getIsCallback() 0 4 2
A saveState() 0 5 1
A loadState() 0 5 1
A loadPageState() 0 6 1
A savePageState() 0 6 1
A isSystemPostField() 0 4 1
A registerRequiresPostData() 0 9 3
A getPostBackEventTarget() 0 10 4
A setPostBackEventTarget() 0 4 1
A getPostBackEventParameter() 0 9 4
A setPostBackEventParameter() 0 4 1
C processPostData() 0 38 14
A getIsLoadingPostData() 0 4 1
A raiseChangedEvents() 0 6 2
A raisePostBackEvent() 0 8 3
A getInFormRender() 0 4 1
A ensureRenderInForm() 0 6 4
A beginFormRender() 0 9 2
B endFormRender() 0 14 6
A setFocus() 0 4 1
A getClientSupportsJavaScript() 0 4 1
A setClientSupportsJavaScript() 0 4 1
A getHead() 0 4 1
A setHead() 0 11 3
A getTitle() 0 8 3
A setTitle() 0 8 2
A getClientState() 0 4 1
A setClientState() 0 4 1
A getRequestClientState() 0 4 1
A getStatePersisterClass() 0 4 1
A setStatePersisterClass() 0 4 1
A getStatePersister() 0 11 3
A getEnableStateValidation() 0 4 1
A setEnableStateValidation() 0 4 1
A getEnableStateEncryption() 0 4 1
A setEnableStateEncryption() 0 4 1
A getEnableStateCompression() 0 4 1
A setEnableStateCompression() 0 4 1
A getPagePath() 0 4 1
A setPagePath() 0 4 1
A registerCachingAction() 0 8 3
A getCachingStack() 0 7 2
A flushWriter() 0 6 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like TPage often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TPage, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * TPage class file
4
 *
5
 * @author Qiang Xue <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @copyright Copyright &copy; 2005-2016 The PRADO Group
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 * @package Prado\Web\UI
10
 */
11
12
namespace Prado\Web\UI;
13
14
use Prado\Collections\TList;
15
use Prado\Collections\TMap;
16
use Prado\Collections\TStack;
17
use Prado\Exceptions\TConfigurationException;
18
use Prado\Exceptions\THttpException;
19
use Prado\Exceptions\TInvalidDataValueException;
20
use Prado\Exceptions\TInvalidDataTypeException;
21
use Prado\Exceptions\TInvalidOperationException;
22
use Prado\Prado;
23
use Prado\TPropertyValue;
24
use Prado\Web\Javascripts\TJavaScript;
25
use Prado\Web\UI\ActiveControls\TActivePageAdapter;
26
use Prado\Web\UI\ActiveControls\TCallbackClientScript;
27
use Prado\Web\UI\WebControls\THead;
28
29
Prado::using('System.Web.UI.*');
30
Prado::using('System.Web.UI.WebControls.*');
31
32
/**
33
 * TPage class
34
 *
35
 * @author Qiang Xue <[email protected]>
36
 * @package Prado\Web\UI
37
 * @since 3.0
38
 */
39
class TPage extends TTemplateControl
40
{
41
	/**
42
	 * system post fields
43
	 */
44
	const FIELD_POSTBACK_TARGET = 'PRADO_POSTBACK_TARGET';
45
	const FIELD_POSTBACK_PARAMETER = 'PRADO_POSTBACK_PARAMETER';
46
	const FIELD_LASTFOCUS = 'PRADO_LASTFOCUS';
47
	const FIELD_PAGESTATE = 'PRADO_PAGESTATE';
48
	const FIELD_CALLBACK_TARGET = 'PRADO_CALLBACK_TARGET';
49
	const FIELD_CALLBACK_PARAMETER = 'PRADO_CALLBACK_PARAMETER';
50
51
	/**
52
	 * @var array system post fields
53
	 */
54
	private static $_systemPostFields = [
55
		'PRADO_POSTBACK_TARGET' => true,
56
		'PRADO_POSTBACK_PARAMETER' => true,
57
		'PRADO_LASTFOCUS' => true,
58
		'PRADO_PAGESTATE' => true,
59
		'PRADO_CALLBACK_TARGET' => true,
60
		'PRADO_CALLBACK_PARAMETER' => true
61
	];
62
	/**
63
	 * @var TForm form instance
64
	 */
65
	private $_form;
66
	/**
67
	 * @var THead head instance
68
	 */
69
	private $_head;
70
	/**
71
	 * @var array list of registered validators
72
	 */
73
	private $_validators = [];
74
	/**
75
	 * @var bool if validation has been performed
76
	 */
77
	private $_validated = false;
78
	/**
79
	 * @var TTheme page theme
80
	 */
81
	private $_theme;
82
	/**
83
	 * @var string page title set when Head is not in page yet
84
	 */
85
	private $_title;
86
	/**
87
	 * @var TTheme page stylesheet theme
88
	 */
89
	private $_styleSheet;
90
	/**
91
	 * @var TClientScriptManager client script manager
92
	 */
93
	private $_clientScript;
94
	/**
95
	 * @var TMap data post back by user
96
	 */
97
	protected $_postData;
98
	/**
99
	 * @var TMap postback data that is not handled during first invocation of LoadPostData.
100
	 */
101
	protected $_restPostData;
102
	/**
103
	 * @var array list of controls whose data have been changed due to the postback
104
	 */
105
	protected $_controlsPostDataChanged = [];
106
	/**
107
	 * @var array list of controls that need to load post data in the current request
108
	 */
109
	protected $_controlsRequiringPostData = [];
110
	/**
111
	 * @var array list of controls that need to load post data in the next postback
112
	 */
113
	protected $_controlsRegisteredForPostData = [];
114
	/**
115
	 * @var TControl control that needs to raise postback event
116
	 */
117
	private $_postBackEventTarget;
118
	/**
119
	 * @var string postback event parameter
120
	 */
121
	private $_postBackEventParameter;
122
	/**
123
	 * @var bool whether the form has been rendered
124
	 */
125
	protected $_formRendered = false;
126
	/**
127
	 * @var bool whether the current rendering is within a form
128
	 */
129
	protected $_inFormRender = false;
130
	/**
131
	 * @var string|TControl the control or the ID of the element on the page to be focused when the page is sent back to user
132
	 */
133
	private $_focus;
134
	/**
135
	 * @var string page path to this page
136
	 */
137
	private $_pagePath = '';
138
	/**
139
	 * @var bool whether page state should be HMAC validated
140
	 */
141
	private $_enableStateValidation = true;
142
	/**
143
	 * @var bool whether page state should be encrypted
144
	 */
145
	private $_enableStateEncryption = false;
146
	/**
147
	 * @var bool whether page state should be compressed
148
	 * @since 3.1.6
149
	 */
150
	private $_enableStateCompression = true;
151
	/**
152
	 * @var string page state persister class name
153
	 */
154
	private $_statePersisterClass = '\Prado\Web\UI\TPageStatePersister';
155
	/**
156
	 * @var mixed page state persister
157
	 */
158
	private $_statePersister;
159
	/**
160
	 * @var TStack stack used to store currently active caching controls
161
	 */
162
	private $_cachingStack;
163
	/**
164
	 * @var string state string to be stored on the client side
165
	 */
166
	private $_clientState = '';
167
	/**
168
	 * @var bool true if loading post data.
169
	 */
170
	protected $_isLoadingPostData = false;
171
	/**
172
	 * @var bool whether client supports javascript
173
	 */
174
	private $_enableJavaScript = true;
175
	/**
176
	 * @var THtmlWriter current html render writer
177
	 */
178
	private $_writer;
179
180
	/**
181
	 * Constructor.
182
	 * Sets the page object to itself.
183
	 * Derived classes must call parent implementation.
184
	 */
185
	public function __construct()
186
	{
187
		$this->setPage($this);
188
	}
189
190
	/**
191
	 * Runs through the page lifecycles.
192
	 * @param THtmlTextWriter $writer the HTML writer
193
	 */
194
	public function run($writer)
195
	{
196
		Prado::trace("Running page life cycles", 'Prado\Web\UI\TPage');
197
		$this->_writer = $writer;
0 ignored issues
show
Documentation Bug introduced by
It seems like $writer of type object<Prado\Web\UI\THtmlTextWriter> is incompatible with the declared type object<Prado\Web\UI\THtmlWriter> of property $_writer.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
198
199
		$this->determinePostBackMode();
200
201
		if ($this->getIsPostBack()) {
202
			if ($this->getIsCallback()) {
203
				$this->processCallbackRequest($writer);
204
			} else {
205
				$this->processPostBackRequest($writer);
206
			}
207
		} else {
208
			$this->processNormalRequest($writer);
209
		}
210
211
		$this->_writer = null;
212
	}
213
214
	protected function processNormalRequest($writer)
215
	{
216
		Prado::trace("Page onPreInit()", 'Prado\Web\UI\TPage');
217
		$this->onPreInit(null);
218
219
		Prado::trace("Page initRecursive()", 'Prado\Web\UI\TPage');
220
		$this->initRecursive();
221
222
		Prado::trace("Page onInitComplete()", 'Prado\Web\UI\TPage');
223
		$this->onInitComplete(null);
224
225
		Prado::trace("Page onPreLoad()", 'Prado\Web\UI\TPage');
226
		$this->onPreLoad(null);
227
		Prado::trace("Page loadRecursive()", 'Prado\Web\UI\TPage');
228
		$this->loadRecursive();
229
		Prado::trace("Page onLoadComplete()", 'Prado\Web\UI\TPage');
230
		$this->onLoadComplete(null);
231
232
		Prado::trace("Page preRenderRecursive()", 'Prado\Web\UI\TPage');
233
		$this->preRenderRecursive();
234
		Prado::trace("Page onPreRenderComplete()", 'Prado\Web\UI\TPage');
235
		$this->onPreRenderComplete(null);
236
237
		Prado::trace("Page savePageState()", 'Prado\Web\UI\TPage');
238
		$this->savePageState();
239
		Prado::trace("Page onSaveStateComplete()", 'Prado\Web\UI\TPage');
240
		$this->onSaveStateComplete(null);
241
242
		Prado::trace("Page renderControl()", 'Prado\Web\UI\TPage');
243
		$this->renderControl($writer);
244
		Prado::trace("Page unloadRecursive()", 'Prado\Web\UI\TPage');
245
		$this->unloadRecursive();
246
	}
247
248
	protected function processPostBackRequest($writer)
249
	{
250
		Prado::trace("Page onPreInit()", 'Prado\Web\UI\TPage');
251
		$this->onPreInit(null);
252
253
		Prado::trace("Page initRecursive()", 'Prado\Web\UI\TPage');
254
		$this->initRecursive();
255
256
		Prado::trace("Page onInitComplete()", 'Prado\Web\UI\TPage');
257
		$this->onInitComplete(null);
258
259
		$this->_restPostData = new TMap;
260
		Prado::trace("Page loadPageState()", 'Prado\Web\UI\TPage');
261
		$this->loadPageState();
262
		Prado::trace("Page processPostData()", 'Prado\Web\UI\TPage');
263
		$this->processPostData($this->_postData, true);
264
		Prado::trace("Page onPreLoad()", 'Prado\Web\UI\TPage');
265
		$this->onPreLoad(null);
266
		Prado::trace("Page loadRecursive()", 'Prado\Web\UI\TPage');
267
		$this->loadRecursive();
268
		Prado::trace("Page processPostData()", 'Prado\Web\UI\TPage');
269
		$this->processPostData($this->_restPostData, false);
270
		Prado::trace("Page raiseChangedEvents()", 'Prado\Web\UI\TPage');
271
		$this->raiseChangedEvents();
272
		Prado::trace("Page raisePostBackEvent()", 'Prado\Web\UI\TPage');
273
		$this->raisePostBackEvent();
274
		Prado::trace("Page onLoadComplete()", 'Prado\Web\UI\TPage');
275
		$this->onLoadComplete(null);
276
277
		Prado::trace("Page preRenderRecursive()", 'Prado\Web\UI\TPage');
278
		$this->preRenderRecursive();
279
		Prado::trace("Page onPreRenderComplete()", 'Prado\Web\UI\TPage');
280
		$this->onPreRenderComplete(null);
281
282
		Prado::trace("Page savePageState()", 'Prado\Web\UI\TPage');
283
		$this->savePageState();
284
		Prado::trace("Page onSaveStateComplete()", 'Prado\Web\UI\TPage');
285
		$this->onSaveStateComplete(null);
286
287
		Prado::trace("Page renderControl()", 'Prado\Web\UI\TPage');
288
		$this->renderControl($writer);
289
		Prado::trace("Page unloadRecursive()", 'Prado\Web\UI\TPage');
290
		$this->unloadRecursive();
291
	}
292
293
	protected static function decodeUTF8($data, $enc)
294
	{
295
		if (is_array($data)) {
296
			foreach ($data as $k => $v) {
297
				$data[$k] = self::decodeUTF8($v, $enc);
298
			}
299
			return $data;
300
		} elseif (is_string($data)) {
301
			return iconv('UTF-8', $enc . '//IGNORE', $data);
302
		} else {
303
			return $data;
304
		}
305
	}
306
307
	/**
308
	 * Sets Adapter to TActivePageAdapter and calls apter to process the
309
	 * callback request.
310
	 * @param mixed $writer
311
	 */
312
	protected function processCallbackRequest($writer)
313
	{
314
		$this->setAdapter(new TActivePageAdapter($this));
315
316
		$callbackEventParameter = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_PARAMETER);
317
		if (strlen($callbackEventParameter) > 0) {
318
			$this->_postData[TPage::FIELD_CALLBACK_PARAMETER] = TJavaScript::jsonDecode((string) $callbackEventParameter);
319
		}
320
321
		// Decode Callback postData from UTF-8 to current Charset
322
		if (($g = $this->getApplication()->getGlobalization(false)) !== null &&
323
			strtoupper($enc = $g->getCharset()) != 'UTF-8') {
324
			foreach ($this->_postData as $k => $v) {
325
				$this->_postData[$k] = self::decodeUTF8($v, $enc);
326
			}
327
		}
328
329
		Prado::trace("Page onPreInit()", 'Prado\Web\UI\TPage');
330
		$this->onPreInit(null);
331
332
		Prado::trace("Page initRecursive()", 'Prado\Web\UI\TPage');
333
		$this->initRecursive();
334
335
		Prado::trace("Page onInitComplete()", 'Prado\Web\UI\TPage');
336
		$this->onInitComplete(null);
337
338
		$this->_restPostData = new TMap;
339
		Prado::trace("Page loadPageState()", 'Prado\Web\UI\TPage');
340
		$this->loadPageState();
341
		Prado::trace("Page processPostData()", 'Prado\Web\UI\TPage');
342
		$this->processPostData($this->_postData, true);
343
		Prado::trace("Page onPreLoad()", 'Prado\Web\UI\TPage');
344
		$this->onPreLoad(null);
345
		Prado::trace("Page loadRecursive()", 'Prado\Web\UI\TPage');
346
		$this->loadRecursive();
347
348
		Prado::trace("Page processPostData()", 'Prado\Web\UI\TPage');
349
		$this->processPostData($this->_restPostData, false);
350
351
		Prado::trace("Page raiseChangedEvents()", 'Prado\Web\UI\TPage');
352
		$this->raiseChangedEvents();
353
354
355
		$this->getAdapter()->processCallbackEvent($writer);
356
357
		/*
358
				Prado::trace("Page raisePostBackEvent()",'Prado\Web\UI\TPage');
359
				$this->raisePostBackEvent();
360
		*/
361
		Prado::trace("Page onLoadComplete()", 'Prado\Web\UI\TPage');
362
		$this->onLoadComplete(null);
363
364
		Prado::trace("Page preRenderRecursive()", 'Prado\Web\UI\TPage');
365
		$this->preRenderRecursive();
366
		Prado::trace("Page onPreRenderComplete()", 'Prado\Web\UI\TPage');
367
		$this->onPreRenderComplete(null);
368
369
		Prado::trace("Page savePageState()", 'Prado\Web\UI\TPage');
370
		$this->savePageState();
371
		Prado::trace("Page onSaveStateComplete()", 'Prado\Web\UI\TPage');
372
		$this->onSaveStateComplete(null);
373
374
		/*
375
				Prado::trace("Page renderControl()",'Prado\Web\UI\TPage');
376
				$this->renderControl($writer);
377
		*/
378
		$this->getAdapter()->renderCallbackResponse($writer);
379
380
		Prado::trace("Page unloadRecursive()", 'Prado\Web\UI\TPage');
381
		$this->unloadRecursive();
382
	}
383
384
	/**
385
	 * Gets the callback client script handler that allows javascript functions
386
	 * to be executed during the callback response.
387
	 * @return TCallbackClientScript interface to client-side javascript code.
388
	 */
389
	public function getCallbackClient()
390
	{
391
		if ($this->getAdapter() !== null) {
392
			return $this->getAdapter()->getCallbackClientHandler();
393
		} else {
394
			return new TCallbackClientScript();
395
		}
396
	}
397
398
	/**
399
	 * Set a new callback client handler.
400
	 * @param TCallbackClientScript $client new callback client script handler.
401
	 */
402
	public function setCallbackClient($client)
403
	{
404
		$this->getAdapter()->setCallbackClientHandler($client);
405
	}
406
407
	/**
408
	 * @return TControl the control responsible for the current callback event,
409
	 * null if nonexistent
410
	 */
411
	public function getCallbackEventTarget()
412
	{
413
		return $this->getAdapter()->getCallbackEventTarget();
414
	}
415
416
	/**
417
	 * Registers a control to raise callback event in the current request.
418
	 * @param TControl $control control registered to raise callback event.
419
	 */
420
	public function setCallbackEventTarget(TControl $control)
421
	{
422
		$this->getAdapter()->setCallbackEventTarget($control);
423
	}
424
425
	/**
426
	 * Callback parameter is decoded assuming JSON encoding.
427
	 * @return string callback event parameter
428
	 */
429
	public function getCallbackEventParameter()
430
	{
431
		return $this->getAdapter()->getCallbackEventParameter();
432
	}
433
434
	/**
435
	 * @param mixed $value callback event parameter
436
	 */
437
	public function setCallbackEventParameter($value)
438
	{
439
		$this->getAdapter()->setCallbackEventParameter($value);
440
	}
441
442
	/**
443
	 * @return TForm the form on the page
444
	 */
445
	public function getForm()
446
	{
447
		return $this->_form;
448
	}
449
450
	/**
451
	 * Registers a TForm instance to the page.
452
	 * Note, a page can contain at most one TForm instance.
453
	 * @param TForm $form the form on the page
454
	 * @throws TInvalidOperationException if this method is invoked twice or more.
455
	 */
456
	public function setForm(TForm $form)
457
	{
458
		if ($this->_form === null) {
459
			$this->_form = $form;
460
		} else {
461
			throw new TInvalidOperationException('page_form_duplicated');
462
		}
463
	}
464
465
	/**
466
	 * Returns a list of registered validators.
467
	 * If validation group is specified, only the validators in that group will be returned.
468
	 * @param null|string $validationGroup validation group
469
	 * @return TList registered validators in the requested group. If the group is null, all validators will be returned.
470
	 */
471
	public function getValidators($validationGroup = null)
472
	{
473
		if (!$this->_validators) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_validators of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
474
			$this->_validators = new TList;
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Prado\Collections\TList() of type object<Prado\Collections\TList> is incompatible with the declared type array of property $_validators.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
475
		}
476
		if (empty($validationGroup) === true) {
477
			return $this->_validators;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->_validators; of type Prado\Collections\TList|array adds the type array to the return on line 477 which is incompatible with the return type documented by Prado\Web\UI\TPage::getValidators of type Prado\Collections\TList.
Loading history...
478
		} else {
479
			$list = new TList;
480
			foreach ($this->_validators as $validator) {
481
				if ($validator->getValidationGroup() === $validationGroup) {
482
					$list->add($validator);
483
				}
484
			}
485
			return $list;
486
		}
487
	}
488
489
	/**
490
	 * Performs input validation.
491
	 * This method will invoke the registered validators to perform the actual validation.
492
	 * If validation group is specified, only the validators in that group will be invoked.
493
	 * @param string $validationGroup validation group. If null, all validators will perform validation.
494
	 */
495
	public function validate($validationGroup = null)
496
	{
497
		Prado::trace("Page validate()", 'Prado\Web\UI\TPage');
498
		$this->_validated = true;
499
		if ($this->_validators && $this->_validators->getCount()) {
0 ignored issues
show
Bug introduced by
The method getCount cannot be called on $this->_validators (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Bug Best Practice introduced by
The expression $this->_validators of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
500
			if ($validationGroup === null) {
501
				foreach ($this->_validators as $validator) {
502
					$validator->validate();
503
				}
504
			} else {
505
				foreach ($this->_validators as $validator) {
506
					if ($validator->getValidationGroup() === $validationGroup) {
507
						$validator->validate();
508
					}
509
				}
510
			}
511
		}
512
	}
513
514
	/**
515
	 * Returns whether user input is valid or not.
516
	 * This method must be invoked after {@link validate} is called.
517
	 * @throws TInvalidOperationException if {@link validate} is not invoked yet.
518
	 * @return bool whether the user input is valid or not.
519
	 */
520
	public function getIsValid()
521
	{
522
		if ($this->_validated) {
523
			if ($this->_validators && $this->_validators->getCount()) {
0 ignored issues
show
Bug introduced by
The method getCount cannot be called on $this->_validators (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Bug Best Practice introduced by
The expression $this->_validators of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
524
				foreach ($this->_validators as $validator) {
525
					if (!$validator->getIsValid()) {
526
						return false;
527
					}
528
				}
529
			}
530
			return true;
531
		} else {
532
			throw new TInvalidOperationException('page_isvalid_unknown');
533
		}
534
	}
535
536
	/**
537
	 * @return TTheme the theme used for the page. Defaults to null.
538
	 */
539 View Code Duplication
	public function getTheme()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
540
	{
541
		if (is_string($this->_theme)) {
542
			$this->_theme = $this->getService()->getThemeManager()->getTheme($this->_theme);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Prado\IService as the method getThemeManager() does only exist in the following implementations of said interface: Prado\Web\Services\TPageService, Prado\Wsat\TWsatService.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
543
		}
544
		return $this->_theme;
545
	}
546
547
	/**
548
	 * Sets the theme to be used for the page.
549
	 * @param string|TTheme $value the theme name or the theme object to be used for the page.
550
	 */
551
	public function setTheme($value)
552
	{
553
		$this->_theme = empty($value) ? null : $value;
0 ignored issues
show
Documentation Bug introduced by
It seems like empty($value) ? null : $value can also be of type string. However, the property $_theme is declared as type object<Prado\Web\UI\TTheme>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
554
	}
555
556
557
	/**
558
	 * @return TTheme the stylesheet theme used for the page. Defaults to null.
559
	 */
560 View Code Duplication
	public function getStyleSheetTheme()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
561
	{
562
		if (is_string($this->_styleSheet)) {
563
			$this->_styleSheet = $this->getService()->getThemeManager()->getTheme($this->_styleSheet);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Prado\IService as the method getThemeManager() does only exist in the following implementations of said interface: Prado\Web\Services\TPageService, Prado\Wsat\TWsatService.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
564
		}
565
		return $this->_styleSheet;
566
	}
567
568
	/**
569
	 * Sets the stylesheet theme to be used for the page.
570
	 * @param string|TTheme $value the stylesheet theme name or the stylesheet theme object to be used for the page.
571
	 */
572
	public function setStyleSheetTheme($value)
573
	{
574
		$this->_styleSheet = empty($value) ? null : $value;
0 ignored issues
show
Documentation Bug introduced by
It seems like empty($value) ? null : $value can also be of type string. However, the property $_styleSheet is declared as type object<Prado\Web\UI\TTheme>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
575
	}
576
577
	/**
578
	 * Applies a skin in the current theme to a control.
579
	 * This method should only be used by framework developers.
580
	 * @param TControl $control a control to be applied skin with
581
	 */
582
	public function applyControlSkin($control)
583
	{
584
		if (($theme = $this->getTheme()) !== null) {
585
			$theme->applySkin($control);
586
		}
587
	}
588
589
	/**
590
	 * Applies a stylesheet skin in the current theme to a control.
591
	 * This method should only be used by framework developers.
592
	 * @param TControl $control a control to be applied stylesheet skin with
593
	 */
594
	public function applyControlStyleSheet($control)
595
	{
596
		if (($theme = $this->getStyleSheetTheme()) !== null) {
597
			$theme->applySkin($control);
598
		}
599
	}
600
601
	/**
602
	 * @return TClientScriptManager client script manager
603
	 */
604
	public function getClientScript()
605
	{
606
		if (!$this->_clientScript) {
607
			$className = $classPath = $this->getService()->getClientScriptManagerClass();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Prado\IService as the method getClientScriptManagerClass() does only exist in the following implementations of said interface: Prado\Web\Services\TPageService, Prado\Wsat\TWsatService.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
Unused Code introduced by
$classPath is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
608
609
			if ($className !== '\Prado\Web\UI\TClientScriptManager' && !is_subclass_of($className, '\Prado\Web\UI\TClientScriptManager')) {
610
				throw new THttpException(404, 'page_csmanagerclass_invalid', $className);
611
			}
612
613
			$this->_clientScript = new $className($this);
614
		}
615
		return $this->_clientScript;
616
	}
617
618
	/**
619
	 * Raises OnPreInit event.
620
	 * This method is invoked right before {@link onInit OnInit} stage.
621
	 * You may override this method to provide additional initialization that
622
	 * should be done before {@link onInit OnInit} (e.g. setting {@link setTheme Theme} or
623
	 * {@link setStyleSheetTheme StyleSheetTheme}).
624
	 * Remember to call the parent implementation to ensure OnPreInit event is raised.
625
	 * @param mixed $param event parameter
626
	 */
627
	public function onPreInit($param)
628
	{
629
		$this->raiseEvent('OnPreInit', $this, $param);
630
	}
631
632
	/**
633
	 * Raises OnInitComplete event.
634
	 * This method is invoked right after {@link onInit OnInit} stage and before {@link onLoad OnLoad} stage.
635
	 * You may override this method to provide additional initialization that
636
	 * should be done after {@link onInit OnInit}.
637
	 * Remember to call the parent implementation to ensure OnInitComplete event is raised.
638
	 * @param mixed $param event parameter
639
	 */
640
	public function onInitComplete($param)
641
	{
642
		$this->raiseEvent('OnInitComplete', $this, $param);
643
	}
644
645
	/**
646
	 * Raises OnPreLoad event.
647
	 * This method is invoked right before {@link onLoad OnLoad} stage.
648
	 * You may override this method to provide additional page loading logic that
649
	 * should be done before {@link onLoad OnLoad}.
650
	 * Remember to call the parent implementation to ensure OnPreLoad event is raised.
651
	 * @param mixed $param event parameter
652
	 */
653
	public function onPreLoad($param)
654
	{
655
		$this->raiseEvent('OnPreLoad', $this, $param);
656
	}
657
658
	/**
659
	 * Raises OnLoadComplete event.
660
	 * This method is invoked right after {@link onLoad OnLoad} stage.
661
	 * You may override this method to provide additional page loading logic that
662
	 * should be done after {@link onLoad OnLoad}.
663
	 * Remember to call the parent implementation to ensure OnLoadComplete event is raised.
664
	 * @param mixed $param event parameter
665
	 */
666
	public function onLoadComplete($param)
667
	{
668
		$this->raiseEvent('OnLoadComplete', $this, $param);
669
	}
670
671
	/**
672
	 * Raises OnPreRenderComplete event.
673
	 * This method is invoked right after {@link onPreRender OnPreRender} stage.
674
	 * You may override this method to provide additional preparation for page rendering
675
	 * that should be done after {@link onPreRender OnPreRender}.
676
	 * Remember to call the parent implementation to ensure OnPreRenderComplete event is raised.
677
	 * @param mixed $param event parameter
678
	 */
679
	public function onPreRenderComplete($param)
680
	{
681
		$this->raiseEvent('OnPreRenderComplete', $this, $param);
682
		$cs = $this->getClientScript();
683
		$theme = $this->getTheme();
684 View Code Duplication
		if ($theme instanceof ITheme) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
685
			foreach ($theme->getStyleSheetFiles() as $url) {
686
				$cs->registerStyleSheetFile($url, $url, $this->getCssMediaType($url));
687
			}
688
			foreach ($theme->getJavaScriptFiles() as $url) {
689
				$cs->registerHeadScriptFile($url, $url);
690
			}
691
		}
692
		$styleSheet = $this->getStyleSheetTheme();
693 View Code Duplication
		if ($styleSheet instanceof ITheme) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
694
			foreach ($styleSheet->getStyleSheetFiles() as $url) {
695
				$cs->registerStyleSheetFile($url, $url, $this->getCssMediaType($url));
696
			}
697
			foreach ($styleSheet->getJavaScriptFiles() as $url) {
698
				$cs->registerHeadScriptFile($url, $url);
699
			}
700
		}
701
702
		if ($cs->getRequiresHead() && $this->getHead() === null) {
703
			throw new TConfigurationException('page_head_required');
704
		}
705
	}
706
707
	/**
708
	 * Determines the media type of the CSS file.
709
	 * The media type is determined according to the following file name pattern:
710
	 *        xxx.media-type.extension
711
	 * For example, 'mystyle.print.css' means its media type is 'print'.
712
	 * @param string $url CSS URL
713
	 * @return string media type of the CSS file
714
	 */
715
	private function getCssMediaType($url)
716
	{
717
		$segs = explode('.', basename($url));
718
		if (isset($segs[2])) {
719
			return $segs[count($segs) - 2];
720
		} else {
721
			return '';
722
		}
723
	}
724
725
	/**
726
	 * Raises OnSaveStateComplete event.
727
	 * This method is invoked right after {@link onSaveState OnSaveState} stage.
728
	 * You may override this method to provide additional logic after page state is saved.
729
	 * Remember to call the parent implementation to ensure OnSaveStateComplete event is raised.
730
	 * @param mixed $param event parameter
731
	 */
732
	public function onSaveStateComplete($param)
733
	{
734
		$this->raiseEvent('OnSaveStateComplete', $this, $param);
735
	}
736
737
	/**
738
	 * Determines whether the current page request is a postback.
739
	 * Call {@link getIsPostBack} to get the result.
740
	 */
741
	private function determinePostBackMode()
742
	{
743
		$postData = $this->getRequest();
744
		if ($postData->contains(self::FIELD_PAGESTATE) || $postData->contains(self::FIELD_POSTBACK_TARGET)) {
745
			$this->_postData = $postData;
0 ignored issues
show
Documentation Bug introduced by
It seems like $postData of type object<Prado\Web\THttpRequest> is incompatible with the declared type object<Prado\Collections\TMap> of property $_postData.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
746
		}
747
	}
748
749
	/**
750
	 * @return bool whether the current page request is a postback
751
	 */
752
	public function getIsPostBack()
753
	{
754
		return $this->_postData !== null;
755
	}
756
757
	/**
758
	 * @return bool whether this is a callback request
759
	 */
760
	public function getIsCallback()
761
	{
762
		return $this->getIsPostBack() && $this->getRequest()->contains(self::FIELD_CALLBACK_TARGET);
763
	}
764
765
	/**
766
	 * This method is invoked when control state is to be saved.
767
	 * You can override this method to do last step state saving.
768
	 * Parent implementation must be invoked.
769
	 */
770
	public function saveState()
771
	{
772
		parent::saveState();
773
		$this->setViewState('ControlsRequiringPostBack', $this->_controlsRegisteredForPostData, []);
774
	}
775
776
	/**
777
	 * This method is invoked right after the control has loaded its state.
778
	 * You can override this method to initialize data from the control state.
779
	 * Parent implementation must be invoked.
780
	 */
781
	public function loadState()
782
	{
783
		parent::loadState();
784
		$this->_controlsRequiringPostData = $this->getViewState('ControlsRequiringPostBack', []);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getViewState('Con...ringPostBack', array()) of type * is incompatible with the declared type array of property $_controlsRequiringPostData.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
785
	}
786
787
	/**
788
	 * Loads page state from persistent storage.
789
	 */
790
	protected function loadPageState()
791
	{
792
		Prado::trace("Loading state", 'Prado\Web\UI\TPage');
793
		$state = $this->getStatePersister()->load();
794
		$this->loadStateRecursive($state, $this->getEnableViewState());
795
	}
796
797
	/**
798
	 * Saves page state from persistent storage.
799
	 */
800
	protected function savePageState()
801
	{
802
		Prado::trace("Saving state", 'Prado\Web\UI\TPage');
803
		$state = &$this->saveStateRecursive($this->getEnableViewState());
804
		$this->getStatePersister()->save($state);
805
	}
806
807
	/**
808
	 * @param string $field the field name
809
	 * @return bool whether the specified field is a system field in postback data
810
	 */
811
	protected function isSystemPostField($field)
812
	{
813
		return isset(self::$_systemPostFields[$field]);
814
	}
815
816
	/**
817
	 * Registers a control for loading post data in the next postback.
818
	 * This method needs to be invoked if the control to load post data
819
	 * may not have a post variable in some cases. For example, a checkbox,
820
	 * if not checked, will not have a post value.
821
	 * @param TControl $control control registered for loading post data
822
	 */
823
	public function registerRequiresPostData($control)
824
	{
825
		$id = is_string($control) ? $control : $control->getUniqueID();
826
		$this->_controlsRegisteredForPostData[$id] = true;
827
		$params = func_get_args();
0 ignored issues
show
Unused Code introduced by
$params is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
828
		foreach ($this->getCachingStack() as $item) {
829
			$item->registerAction('Page', 'registerRequiresPostData', [$id]);
830
		}
831
	}
832
833
	/**
834
	 * @return TControl the control responsible for the current postback event, null if nonexistent
835
	 */
836
	public function getPostBackEventTarget()
837
	{
838
		if ($this->_postBackEventTarget === null && $this->_postData !== null) {
839
			$eventTarget = $this->_postData->itemAt(self::FIELD_POSTBACK_TARGET);
840
			if (!empty($eventTarget)) {
841
				$this->_postBackEventTarget = $this->findControl($eventTarget);
842
			}
843
		}
844
		return $this->_postBackEventTarget;
845
	}
846
847
	/**
848
	 * Registers a control to raise postback event in the current request.
849
	 * @param TControl $control control registered to raise postback event.
850
	 */
851
	public function setPostBackEventTarget(TControl $control)
852
	{
853
		$this->_postBackEventTarget = $control;
854
	}
855
856
	/**
857
	 * @return string postback event parameter
858
	 */
859
	public function getPostBackEventParameter()
860
	{
861
		if ($this->_postBackEventParameter === null && $this->_postData !== null) {
862
			if (($this->_postBackEventParameter = $this->_postData->itemAt(self::FIELD_POSTBACK_PARAMETER)) === null) {
863
				$this->_postBackEventParameter = '';
864
			}
865
		}
866
		return $this->_postBackEventParameter;
867
	}
868
869
	/**
870
	 * @param string $value postback event parameter
871
	 */
872
	public function setPostBackEventParameter($value)
873
	{
874
		$this->_postBackEventParameter = $value;
875
	}
876
877
	/**
878
	 * Processes post data.
879
	 * @param TMap $postData post data to be processed
880
	 * @param bool $beforeLoad whether this method is invoked before {@link onLoad OnLoad}.
881
	 */
882
	protected function processPostData($postData, $beforeLoad)
883
	{
884
		$this->_isLoadingPostData = true;
885
		if ($beforeLoad) {
886
			$this->_restPostData = new TMap;
887
		}
888
		foreach ($postData as $key => $value) {
889
			if ($this->isSystemPostField($key)) {
890
				continue;
891
			} elseif ($control = $this->findControl($key)) {
892
				if ($control instanceof \Prado\Web\UI\IPostBackDataHandler) {
893
					if ($control->loadPostData($key, $postData)) {
894
						$this->_controlsPostDataChanged[] = $control;
895
					}
896
				} elseif ($control instanceof IPostBackEventHandler &&
897
					empty($this->_postData[self::FIELD_POSTBACK_TARGET])) {
898
					$this->_postData->add(self::FIELD_POSTBACK_TARGET, $key);  // not calling setPostBackEventTarget() because the control may be removed later
899
				}
900
				unset($this->_controlsRequiringPostData[$key]);
901
			} elseif ($beforeLoad) {
902
				$this->_restPostData->add($key, $value);
903
			}
904
		}
905
906
		foreach ($this->_controlsRequiringPostData as $key => $value) {
907
			if ($control = $this->findControl($key)) {
908
				if ($control instanceof \Prado\Web\UI\IPostBackDataHandler) {
909
					if ($control->loadPostData($key, $this->_postData)) {
910
						$this->_controlsPostDataChanged[] = $control;
911
					}
912
				} else {
913
					throw new TInvalidDataValueException('page_postbackcontrol_invalid', $key);
914
				}
915
				unset($this->_controlsRequiringPostData[$key]);
916
			}
917
		}
918
		$this->_isLoadingPostData = false;
919
	}
920
921
	/**
922
	 * @return bool true if loading post data.
923
	 */
924
	public function getIsLoadingPostData()
925
	{
926
		return $this->_isLoadingPostData;
927
	}
928
929
	/**
930
	 * Raises OnPostDataChangedEvent for controls whose data have been changed due to the postback.
931
	 */
932
	protected function raiseChangedEvents()
933
	{
934
		foreach ($this->_controlsPostDataChanged as $control) {
935
			$control->raisePostDataChangedEvent();
936
		}
937
	}
938
939
	/**
940
	 * Raises PostBack event.
941
	 */
942
	protected function raisePostBackEvent()
943
	{
944
		if (($postBackHandler = $this->getPostBackEventTarget()) === null) {
945
			$this->validate();
946
		} elseif ($postBackHandler instanceof IPostBackEventHandler) {
947
			$postBackHandler->raisePostBackEvent($this->getPostBackEventParameter());
948
		}
949
	}
950
951
	/**
952
	 * @return bool Whether form rendering is in progress
953
	 */
954
	public function getInFormRender()
955
	{
956
		return $this->_inFormRender;
957
	}
958
959
	/**
960
	 * Ensures the control is rendered within a form.
961
	 * @param TControl $control the control to be rendered
962
	 * @throws TConfigurationException if the control is outside of the form
963
	 */
964
	public function ensureRenderInForm($control)
965
	{
966
		if (!$this->getIsCallback() && !$this->_inFormRender) {
967
			throw new TConfigurationException('page_control_outofform', get_class($control), $control ? $control->getUniqueID() : null);
968
		}
969
	}
970
971
	/**
972
	 * @internal This method is invoked by TForm at the beginning of its rendering
973
	 * @param mixed $writer
974
	 */
975
	public function beginFormRender($writer)
976
	{
977
		if ($this->_formRendered) {
978
			throw new TConfigurationException('page_form_duplicated');
979
		}
980
		$this->_formRendered = true;
981
		$this->getClientScript()->registerHiddenField(self::FIELD_PAGESTATE, $this->getClientState());
982
		$this->_inFormRender = true;
983
	}
984
985
	/**
986
	 * @internal This method is invoked by TForm  at the end of its rendering
987
	 * @param mixed $writer
988
	 */
989
	public function endFormRender($writer)
990
	{
991
		if ($this->_focus) {
992
			if (($this->_focus instanceof TControl) && $this->_focus->getVisible(true)) {
993
				$focus = $this->_focus->getClientID();
994
			} else {
995
				$focus = $this->_focus;
996
			}
997
			$this->getClientScript()->registerFocusControl($focus);
998
		} elseif ($this->_postData && ($lastFocus = $this->_postData->itemAt(self::FIELD_LASTFOCUS)) !== null) {
999
			$this->getClientScript()->registerFocusControl($lastFocus);
1000
		}
1001
		$this->_inFormRender = false;
1002
	}
1003
1004
	/**
1005
	 * Sets input focus on a control after the page is rendered to users.
1006
	 * @param string|TControl $value control to receive focus, or the ID of the element on the page to receive focus
1007
	 */
1008
	public function setFocus($value)
1009
	{
1010
		$this->_focus = $value;
1011
	}
1012
1013
	/**
1014
	 * @return bool whether client supports javascript. Defaults to true.
1015
	 */
1016
	public function getClientSupportsJavaScript()
1017
	{
1018
		return $this->_enableJavaScript;
1019
	}
1020
1021
	/**
1022
	 * @param bool $value whether client supports javascript. If false, javascript will not be generated for controls.
1023
	 */
1024
	public function setClientSupportsJavaScript($value)
1025
	{
1026
		$this->_enableJavaScript = TPropertyValue::ensureBoolean($value);
1027
	}
1028
1029
	/**
1030
	 * @return THead page head, null if not available
1031
	 */
1032
	public function getHead()
1033
	{
1034
		return $this->_head;
1035
	}
1036
1037
	/**
1038
	 * @param THead $value page head
1039
	 * @throws TInvalidOperationException if a head already exists
1040
	 */
1041
	public function setHead(THead $value)
1042
	{
1043
		if ($this->_head) {
1044
			throw new TInvalidOperationException('page_head_duplicated');
1045
		}
1046
		$this->_head = $value;
1047
		if ($this->_title !== null) {
1048
			$this->_head->setTitle($this->_title);
1049
			$this->_title = null;
1050
		}
1051
	}
1052
1053
	/**
1054
	 * @return string page title.
1055
	 */
1056
	public function getTitle()
1057
	{
1058
		if ($this->_head) {
1059
			return $this->_head->getTitle();
1060
		} else {
1061
			return $this->_title === null ? '' : $this->_title;
1062
		}
1063
	}
1064
1065
	/**
1066
	 * Sets the page title.
1067
	 * Note, a {@link THead} control needs to place on the page
1068
	 * in order that this title be rendered.
1069
	 * @param string $value page title. This will override the title set in {@link getHead Head}.
1070
	 */
1071
	public function setTitle($value)
1072
	{
1073
		if ($this->_head) {
1074
			$this->_head->setTitle($value);
1075
		} else {
1076
			$this->_title = $value;
1077
		}
1078
	}
1079
1080
	/**
1081
	 * Returns the state to be stored on the client side.
1082
	 * This method should only be used by framework and control developers.
1083
	 * @return string the state to be stored on the client side
1084
	 */
1085
	public function getClientState()
1086
	{
1087
		return $this->_clientState;
1088
	}
1089
1090
	/**
1091
	 * Sets the state to be stored on the client side.
1092
	 * This method should only be used by framework and control developers.
1093
	 * @param string $state the state to be stored on the client side
1094
	 */
1095
	public function setClientState($state)
1096
	{
1097
		$this->_clientState = $state;
1098
	}
1099
1100
	/**
1101
	 * @return string the state postback from client side
1102
	 */
1103
	public function getRequestClientState()
1104
	{
1105
		return $this->getRequest()->itemAt(self::FIELD_PAGESTATE);
1106
	}
1107
1108
	/**
1109
	 * @return string class name of the page state persister. Defaults to TPageStatePersister.
1110
	 */
1111
	public function getStatePersisterClass()
1112
	{
1113
		return $this->_statePersisterClass;
1114
	}
1115
1116
	/**
1117
	 * @param string $value class name of the page state persister.
1118
	 */
1119
	public function setStatePersisterClass($value)
1120
	{
1121
		$this->_statePersisterClass = $value;
1122
	}
1123
1124
	/**
1125
	 * @return IPageStatePersister page state persister
1126
	 */
1127
	public function getStatePersister()
1128
	{
1129
		if ($this->_statePersister === null) {
1130
			$this->_statePersister = Prado::createComponent($this->_statePersisterClass);
1131
			if (!($this->_statePersister instanceof IPageStatePersister)) {
1132
				throw new TInvalidDataTypeException('page_statepersister_invalid');
1133
			}
1134
			$this->_statePersister->setPage($this);
1135
		}
1136
		return $this->_statePersister;
1137
	}
1138
1139
	/**
1140
	 * @return bool whether page state should be HMAC validated. Defaults to true.
1141
	 */
1142
	public function getEnableStateValidation()
1143
	{
1144
		return $this->_enableStateValidation;
1145
	}
1146
1147
	/**
1148
	 * @param bool $value whether page state should be HMAC validated.
1149
	 */
1150
	public function setEnableStateValidation($value)
1151
	{
1152
		$this->_enableStateValidation = TPropertyValue::ensureBoolean($value);
1153
	}
1154
1155
	/**
1156
	 * @return bool whether page state should be encrypted. Defaults to false.
1157
	 */
1158
	public function getEnableStateEncryption()
1159
	{
1160
		return $this->_enableStateEncryption;
1161
	}
1162
1163
	/**
1164
	 * @param bool $value whether page state should be encrypted.
1165
	 */
1166
	public function setEnableStateEncryption($value)
1167
	{
1168
		$this->_enableStateEncryption = TPropertyValue::ensureBoolean($value);
1169
	}
1170
1171
	/**
1172
	 * @return bool whether page state should be compressed. Defaults to true.
1173
	 * @since 3.1.6
1174
	 */
1175
	public function getEnableStateCompression()
1176
	{
1177
		return $this->_enableStateCompression;
1178
	}
1179
1180
	/**
1181
	 * @param bool $value whether page state should be compressed.
1182
	 * @since 3.1.6
1183
	 */
1184
	public function setEnableStateCompression($value)
1185
	{
1186
		$this->_enableStateCompression = TPropertyValue::ensureBoolean($value);
1187
	}
1188
1189
	/**
1190
	 * @return string the requested page path for this page
1191
	 */
1192
	public function getPagePath()
1193
	{
1194
		return $this->_pagePath;
1195
	}
1196
1197
	/**
1198
	 * @param string $value the requested page path for this page
1199
	 */
1200
	public function setPagePath($value)
1201
	{
1202
		$this->_pagePath = $value;
1203
	}
1204
1205
	/**
1206
	 * Registers an action associated with the content being cached.
1207
	 * The registered action will be replayed if the content stored
1208
	 * in the cache is served to end-users.
1209
	 * @param string $context context of the action method. This is a property-path
1210
	 * referring to the context object (e.g. Page, Page.ClientScript).
1211
	 * @param string $funcName method name of the context object
1212
	 * @param array $funcParams list of parameters to be passed to the action method
1213
	 */
1214
	public function registerCachingAction($context, $funcName, $funcParams)
1215
	{
1216
		if ($this->_cachingStack) {
1217
			foreach ($this->_cachingStack as $cache) {
1218
				$cache->registerAction($context, $funcName, $funcParams);
1219
			}
1220
		}
1221
	}
1222
1223
	/**
1224
	 * @return TStack stack of {@link TOutputCache} objects
1225
	 */
1226
	public function getCachingStack()
1227
	{
1228
		if (!$this->_cachingStack) {
1229
			$this->_cachingStack = new TStack;
1230
		}
1231
		return $this->_cachingStack;
1232
	}
1233
1234
	/**
1235
	 * Flushes output
1236
	 */
1237
	public function flushWriter()
1238
	{
1239
		if ($this->_writer) {
1240
			$this->Response->write($this->_writer->flush());
1241
		}
1242
	}
1243
}
1244