Completed
Push — 1.10.x ( 9a71ab...f8e8ec )
by
unknown
50:16
created

xajax::errorHandlerOn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * xajax.inc.php :: Main xajax class and setup file
4
 *
5
 * xajax version 0.2.4
6
 * copyright (c) 2005 by Jared White & J. Max Wilson
7
 * http://www.xajaxproject.org
8
 *
9
 * xajax is an open source PHP class library for easily creating powerful
10
 * PHP-driven, web-based Ajax Applications. Using xajax, you can asynchronously
11
 * call PHP functions and update the content of your your webpage without
12
 * reloading the page.
13
 *
14
 * xajax is released under the terms of the LGPL license
15
 * http://www.gnu.org/copyleft/lesser.html#SEC3
16
 *
17
 * This library is free software; you can redistribute it and/or
18
 * modify it under the terms of the GNU Lesser General Public
19
 * License as published by the Free Software Foundation; either
20
 * version 2.1 of the License, or (at your option) any later version.
21
 *
22
 * This library is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25
 * Lesser General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU Lesser General Public
28
 * License along with this library; if not, write to the Free Software
29
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
30
 *
31
 * @package chamilo.include.xajax
32
 * @version $Id: xajax.inc.php,v 1.1 2006/07/21 15:29:46 elixir_inter Exp $
33
 * @copyright Copyright (c) 2005-2006  by Jared White & J. Max Wilson
34
 * @license http://www.gnu.org/copyleft/lesser.html#SEC3 LGPL License
35
 */
36
37
/*
38
   ----------------------------------------------------------------------------
39
   | Online documentation for this class is available on the xajax wiki at:   |
40
   | http://wiki.xajaxproject.org/Documentation:xajax.inc.php                 |
41
   ----------------------------------------------------------------------------
42
*/
43
44
/**
45
 * Define XAJAX_DEFAULT_CHAR_ENCODING that is used by both
46
 * the xajax and xajaxResponse classes
47
 */
48
if (!defined ('XAJAX_DEFAULT_CHAR_ENCODING'))
49
{
50
	define ('XAJAX_DEFAULT_CHAR_ENCODING', 'utf-8' );
51
}
52
53
/**
54
 * Communication Method Defines
55
 */
56
if (!defined ('XAJAX_GET'))
57
{
58
	define ('XAJAX_GET', 0);
59
}
60
if (!defined ('XAJAX_POST'))
61
{
62
	define ('XAJAX_POST', 1);
63
}
64
65
/**
66
 * The xajax class generates the xajax javascript for your page including the
67
 * Javascript wrappers for the PHP functions that you want to call from your page.
68
 * It also handles processing and executing the command messages in the XML responses
69
 * sent back to your page from your PHP functions.
70
 *
71
 * @package chamilo.include.xajax
72
 */
73
class xajax
74
{
75
	/**#@+
76
	 * @access protected
77
	 */
78
	/**
79
	 * @var array Array of PHP functions that will be callable through javascript wrappers
80
	 */
81
	var $aFunctions;
82
	/**
83
	 * @var array Array of object callbacks that will allow Javascript to call PHP methods (key=function name)
84
	 */
85
	var $aObjects;
86
	/**
87
	 * @var array Array of RequestTypes to be used with each function (key=function name)
88
	 */
89
	var $aFunctionRequestTypes;
90
	/**
91
	 * @var array Array of Include Files for any external functions (key=function name)
92
	 */
93
	var $aFunctionIncludeFiles;
94
	/**
95
	 * @var string Name of the PHP function to call if no callable function was found
96
	 */
97
	var $sCatchAllFunction;
98
	/**
99
	 * @var string Name of the PHP function to call before any other function
100
	 */
101
	var $sPreFunction;
102
	/**
103
	 * @var string The URI for making requests to the xajax object
104
	 */
105
	var $sRequestURI;
106
	/**
107
	 * @var string The prefix to prepend to the javascript wraper function name
108
	 */
109
	var $sWrapperPrefix;
110
	/**
111
	 * @var boolean Show debug messages (default false)
112
	 */
113
	var $bDebug;
114
	/**
115
	 * @var boolean Show messages in the client browser's status bar (default false)
116
	 */
117
	var $bStatusMessages;
118
	/**
119
	 * @var boolean Allow xajax to exit after processing a request (default true)
120
	 */
121
	var $bExitAllowed;
122
	/**
123
	 * @var boolean Use wait cursor in browser (default true)
124
	 */
125
	var $bWaitCursor;
126
	/**
127
	 * @var boolean Use an special xajax error handler so the errors are sent to the browser properly (default false)
128
	 */
129
	var $bErrorHandler;
130
	/**
131
	 * @var string Specify what, if any, file xajax should log errors to (and more information in a future release)
132
	 */
133
	var $sLogFile;
134
	/**
135
	 * @var boolean Clean all output buffers before outputting response (default false)
136
	 */
137
	var $bCleanBuffer;
138
	/**
139
	 * @var string String containing the character encoding used
140
	 */
141
	var $sEncoding;
142
	/**
143
	 * @var boolean Decode input request args from UTF-8 (default false)
144
	 */
145
	var $bDecodeUTF8Input;
146
	/**
147
	 * @var boolean Convert special characters to HTML entities (default false)
148
	 */
149
	var $bOutputEntities;
150
	/**
151
	 * @var array Array for parsing complex objects
152
	 */
153
	var $aObjArray;
154
	/**
155
	 * @var integer Position in $aObjArray
156
	 */
157
	var $iPos;
158
159
	/**#@-*/
160
161
	/**
162
	 * Constructor. You can set some extra xajax options right away or use
163
	 * individual methods later to set options.
164
	 *
165
	 * @param string  defaults to the current browser URI
166
	 * @param string  defaults to "xajax_";
167
	 * @param string  defaults to XAJAX_DEFAULT_CHAR_ENCODING defined above
168
	 * @param boolean defaults to false
169
	 */
170
	public function __construct($sRequestURI="",$sWrapperPrefix="xajax_",$sEncoding=XAJAX_DEFAULT_CHAR_ENCODING,$bDebug=false)
171
	{
172
		$this->aFunctions = array();
173
		$this->aObjects = array();
174
		$this->aFunctionIncludeFiles = array();
175
		$this->sRequestURI = $sRequestURI;
176
		if ($this->sRequestURI == "")
177
			$this->sRequestURI = $this->_detectURI();
178
		$this->sWrapperPrefix = $sWrapperPrefix;
179
		$this->bDebug = $bDebug;
180
		$this->bStatusMessages = false;
181
		$this->bWaitCursor = true;
182
		$this->bExitAllowed = true;
183
		$this->bErrorHandler = false;
184
		$this->sLogFile = "";
185
		$this->bCleanBuffer = false;
186
		$this->setCharEncoding($sEncoding);
187
		$this->bDecodeUTF8Input = false;
188
		$this->bOutputEntities = false;
189
	}
190
191
	/**
192
	 * Sets the URI to which requests will be made.
193
	 * <i>Usage:</i> <kbd>$xajax->setRequestURI("http://www.xajaxproject.org");</kbd>
194
	 *
195
	 * @param string the URI (can be absolute or relative) of the PHP script
196
	 *               that will be accessed when an xajax request occurs
197
	 */
198
	function setRequestURI($sRequestURI)
199
	{
200
		$this->sRequestURI = $sRequestURI;
201
	}
202
203
	/**
204
	 * Sets the prefix that will be appended to the Javascript wrapper
205
	 * functions (default is "xajax_").
206
	 *
207
	 * @param string
208
	 */
209
	//
210
	function setWrapperPrefix($sPrefix)
211
	{
212
		$this->sWrapperPrefix = $sPrefix;
213
	}
214
215
	/**
216
	 * Enables debug messages for xajax.
217
	 * */
218
	function debugOn()
219
	{
220
		$this->bDebug = true;
221
	}
222
223
	/**
224
	 * Disables debug messages for xajax (default behavior).
225
	 */
226
	function debugOff()
227
	{
228
		$this->bDebug = false;
229
	}
230
231
	/**
232
	 * Enables messages in the browser's status bar for xajax.
233
	 */
234
	function statusMessagesOn()
235
	{
236
		$this->bStatusMessages = true;
237
	}
238
239
	/**
240
	 * Disables messages in the browser's status bar for xajax (default behavior).
241
	 */
242
	function statusMessagesOff()
243
	{
244
		$this->bStatusMessages = false;
245
	}
246
247
	/**
248
	 * Enables the wait cursor to be displayed in the browser (default behavior).
249
	 */
250
	function waitCursorOn()
251
	{
252
		$this->bWaitCursor = true;
253
	}
254
255
	/**
256
	 * Disables the wait cursor to be displayed in the browser.
257
	 */
258
	function waitCursorOff()
259
	{
260
		$this->bWaitCursor = false;
261
	}
262
263
	/**
264
	 * Enables xajax to exit immediately after processing a request and
265
	 * sending the response back to the browser (default behavior).
266
	 */
267
	function exitAllowedOn()
268
	{
269
		$this->bExitAllowed = true;
270
	}
271
272
	/**
273
	 * Disables xajax's default behavior of exiting immediately after
274
	 * processing a request and sending the response back to the browser.
275
	 */
276
	function exitAllowedOff()
277
	{
278
		$this->bExitAllowed = false;
279
	}
280
281
	/**
282
	 * Turns on xajax's error handling system so that PHP errors that occur
283
	 * during a request are trapped and pushed to the browser in the form of
284
	 * a Javascript alert.
285
	 */
286
	function errorHandlerOn()
287
	{
288
		$this->bErrorHandler = true;
289
	}
290
291
	/**
292
	 * Turns off xajax's error handling system (default behavior).
293
	 */
294
	function errorHandlerOff()
295
	{
296
		$this->bErrorHandler = false;
297
	}
298
299
	/**
300
	 * Specifies a log file that will be written to by xajax during a request
301
	 * (used only by the error handling system at present). If you don't invoke
302
	 * this method, or you pass in "", then no log file will be written to.
303
	 * <i>Usage:</i> <kbd>$xajax->setLogFile("/xajax_logs/errors.log");</kbd>
304
	 */
305
	function setLogFile($sFilename)
306
	{
307
		$this->sLogFile = $sFilename;
308
	}
309
310
	/**
311
	 * Causes xajax to clean out all output buffers before outputting a
312
	 * response (default behavior).
313
	 */
314
	function cleanBufferOn()
315
	{
316
		$this->bCleanBuffer = true;
317
	}
318
	/**
319
	 * Turns off xajax's output buffer cleaning.
320
	 */
321
	function cleanBufferOff()
322
	{
323
		$this->bCleanBuffer = false;
324
	}
325
326
	/**
327
	 * Sets the character encoding for the HTTP output based on
328
	 * <kbd>$sEncoding</kbd>, which is a string containing the character
329
	 * encoding to use. You don't need to use this method normally, since the
330
	 * character encoding for the response gets set automatically based on the
331
	 * <kbd>XAJAX_DEFAULT_CHAR_ENCODING</kbd> constant.
332
	 * <i>Usage:</i> <kbd>$xajax->setCharEncoding("utf-8");</kbd>
333
	 *
334
	 * @param string the encoding type to use (utf-8, iso-8859-1, etc.)
335
	 */
336
	function setCharEncoding($sEncoding)
337
	{
338
		$this->sEncoding = $sEncoding;
339
	}
340
341
	/**
342
	 * Causes xajax to decode the input request args from UTF-8 to the current
343
	 * encoding if possible. Either the iconv or mb_string extension must be
344
	 * present for optimal functionality.
345
	 */
346
	function decodeUTF8InputOn()
347
	{
348
		$this->bDecodeUTF8Input = true;
349
	}
350
351
	/**
352
	 * Turns off decoding the input request args from UTF-8 (default behavior).
353
	 */
354
	function decodeUTF8InputOff()
355
	{
356
		$this->bDecodeUTF8Input = false;
357
	}
358
359
	/**
360
	 * Tells the response object to convert special characters to HTML entities
361
	 * automatically (only works if the mb_string extension is available).
362
	 */
363
	function outputEntitiesOn()
364
	{
365
		$this->bOutputEntities = true;
366
	}
367
368
	/**
369
	 * Tells the response object to output special characters intact. (default
370
	 * behavior).
371
	 */
372
	function outputEntitiesOff()
373
	{
374
		$this->bOutputEntities = false;
375
	}
376
377
	/**
378
	 * Registers a PHP function or method to be callable through xajax in your
379
	 * Javascript. If you want to register a function, pass in the name of that
380
	 * function. If you want to register a static class method, pass in an
381
	 * array like so:
382
	 * <kbd>array("myFunctionName", "myClass", "myMethod")</kbd>
383
	 * For an object instance method, use an object variable for the second
384
	 * array element (and in PHP 4 make sure you put an & before the variable
385
	 * to pass the object by reference). Note: the function name is what you
386
	 * call via Javascript, so it can be anything as long as it doesn't
387
	 * conflict with any other registered function name.
388
	 *
389
	 * <i>Usage:</i> <kbd>$xajax->registerFunction("myFunction");</kbd>
390
	 * or: <kbd>$xajax->registerFunction(array("myFunctionName", &$myObject, "myMethod"));</kbd>
391
	 *
392
	 * @param mixed  contains the function name or an object callback array
393
	 * @param mixed  request type (XAJAX_GET/XAJAX_POST) that should be used
394
	 *               for this function.  Defaults to XAJAX_POST.
395
	 */
396
	function registerFunction($mFunction,$sRequestType=XAJAX_POST)
397
	{
398
		if (is_array($mFunction)) {
399
			$this->aFunctions[$mFunction[0]] = 1;
400
			$this->aFunctionRequestTypes[$mFunction[0]] = $sRequestType;
401
			$this->aObjects[$mFunction[0]] = array_slice($mFunction, 1);
402
		}
403
		else {
404
			$this->aFunctions[$mFunction] = 1;
405
			$this->aFunctionRequestTypes[$mFunction] = $sRequestType;
406
		}
407
	}
408
409
	/**
410
	 * Registers a PHP function to be callable through xajax which is located
411
	 * in some other file.  If the function is requested the external file will
412
	 * be included to define the function before the function is called.
413
	 *
414
	 * <i>Usage:</i> <kbd>$xajax->registerExternalFunction("myFunction","myFunction.inc.php",XAJAX_POST);</kbd>
415
	 *
416
	 * @param string contains the function name or an object callback array
417
	 *               ({@link xajax::registerFunction() see registerFunction} for
418
	 *               more info on object callback arrays)
419
	 * @param string contains the path and filename of the include file
420
	 * @param mixed  the RequestType (XAJAX_GET/XAJAX_POST) that should be used
421
	 *		          for this function. Defaults to XAJAX_POST.
422
	 */
423
	function registerExternalFunction($mFunction,$sIncludeFile,$sRequestType=XAJAX_POST)
424
	{
425
		$this->registerFunction($mFunction, $sRequestType);
426
427
		if (is_array($mFunction)) {
428
			$this->aFunctionIncludeFiles[$mFunction[0]] = $sIncludeFile;
429
		}
430
		else {
431
			$this->aFunctionIncludeFiles[$mFunction] = $sIncludeFile;
432
		}
433
	}
434
435
	/**
436
	 * Registers a PHP function to be called when xajax cannot find the
437
	 * function being called via Javascript. Because this is technically
438
	 * impossible when using "wrapped" functions, the catch-all feature is
439
	 * only useful when you're directly using the xajax.call() Javascript
440
	 * method. Use the catch-all feature when you want more dynamic ability to
441
	 * intercept unknown calls and handle them in a custom way.
442
	 *
443
	 * <i>Usage:</i> <kbd>$xajax->registerCatchAllFunction("myCatchAllFunction");</kbd>
444
	 *
445
	 * @param string contains the function name or an object callback array
446
	 *               ({@link xajax::registerFunction() see registerFunction} for
447
	 *               more info on object callback arrays)
448
	 */
449 View Code Duplication
	function registerCatchAllFunction($mFunction)
450
	{
451
		if (is_array($mFunction)) {
452
			$this->sCatchAllFunction = $mFunction[0];
453
			$this->aObjects[$mFunction[0]] = array_slice($mFunction, 1);
454
		}
455
		else {
456
			$this->sCatchAllFunction = $mFunction;
457
		}
458
	}
459
460
	/**
461
	 * Registers a PHP function to be called before xajax calls the requested
462
	 * function. xajax will automatically add the request function's response
463
	 * to the pre-function's response to create a single response. Another
464
	 * feature is the ability to return not just a response, but an array with
465
	 * the first element being false (a boolean) and the second being the
466
	 * response. In this case, the pre-function's response will be returned to
467
	 * the browser without xajax calling the requested function.
468
	 *
469
	 * <i>Usage:</i> <kbd>$xajax->registerPreFunction("myPreFunction");</kbd>
470
	 *
471
	 * @param string contains the function name or an object callback array
472
	 *               ({@link xajax::registerFunction() see registerFunction} for
473
	 *               more info on object callback arrays)
474
	 */
475 View Code Duplication
	function registerPreFunction($mFunction)
476
	{
477
		if (is_array($mFunction)) {
478
			$this->sPreFunction = $mFunction[0];
479
			$this->aObjects[$mFunction[0]] = array_slice($mFunction, 1);
480
		}
481
		else {
482
			$this->sPreFunction = $mFunction;
483
		}
484
	}
485
486
	/**
487
	 * Returns true if xajax can process the request, false if otherwise.
488
	 * You can use this to determine if xajax needs to process the request or
489
	 * not.
490
	 *
491
	 * @return boolean
492
	 */
493
	function canProcessRequests()
494
	{
495
		if ($this->getRequestMode() != -1) return true;
496
		return false;
497
	}
498
499
	/**
500
	 * Returns the current request mode (XAJAX_GET or XAJAX_POST), or -1 if
501
	 * there is none.
502
	 *
503
	 * @return mixed
504
	 */
505
	function getRequestMode()
506
	{
507
		if (!empty($_GET["xajax"]))
508
			return XAJAX_GET;
509
510
		if (!empty($_POST["xajax"]))
511
			return XAJAX_POST;
512
513
		return -1;
514
	}
515
516
	/**
517
	 * This is the main communications engine of xajax. The engine handles all
518
	 * incoming xajax requests, calls the apporiate PHP functions (or
519
	 * class/object methods) and passes the XML responses back to the
520
	 * Javascript response handler. If your RequestURI is the same as your Web
521
	 * page then this function should be called before any headers or HTML has
522
	 * been sent.
523
	 */
524
	function processRequests()
525
	{
526
527
		$requestMode = -1;
528
		$sFunctionName = "";
529
		$bFoundFunction = true;
530
		$bFunctionIsCatchAll = false;
531
		$sFunctionNameForSpecial = "";
532
		$aArgs = array();
533
		$sPreResponse = "";
534
		$bEndRequest = false;
535
		$sResponse = "";
536
537
		$requestMode = $this->getRequestMode();
538
		if ($requestMode == -1) return;
539
540
		if ($requestMode == XAJAX_POST)
541
		{
542
			$sFunctionName = $_POST["xajax"];
543
544
			if (!empty($_POST["xajaxargs"]))
545
				$aArgs = $_POST["xajaxargs"];
546
		}
547
		else
548
		{
549
			header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
550
			header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
551
			header ("Cache-Control: no-cache, must-revalidate");
552
			header ("Pragma: no-cache");
553
554
			$sFunctionName = $_GET["xajax"];
555
556
			if (!empty($_GET["xajaxargs"]))
557
				$aArgs = $_GET["xajaxargs"];
558
		}
559
560
		// Use xajax error handler if necessary
561
		if ($this->bErrorHandler) {
562
			$GLOBALS['xajaxErrorHandlerText'] = "";
563
			set_error_handler("xajaxErrorHandler");
564
		}
565
566
		if ($this->sPreFunction) {
567
			if (!$this->_isFunctionCallable($this->sPreFunction)) {
568
				$bFoundFunction = false;
569
				$objResponse = new xajaxResponse();
570
				$objResponse->addAlert("Unknown Pre-Function ". $this->sPreFunction);
571
				$sResponse = $objResponse->getXML();
572
			}
573
		}
574
		//include any external dependencies associated with this function name
575
		if (array_key_exists($sFunctionName,$this->aFunctionIncludeFiles))
576
		{
577
			ob_start();
578
			include_once($this->aFunctionIncludeFiles[$sFunctionName]);
579
			ob_end_clean();
580
		}
581
582
		if ($bFoundFunction) {
583
			$sFunctionNameForSpecial = $sFunctionName;
584
			if (!array_key_exists($sFunctionName, $this->aFunctions))
585
			{
586
				if ($this->sCatchAllFunction) {
587
					$sFunctionName = $this->sCatchAllFunction;
588
					$bFunctionIsCatchAll = true;
589
				}
590 View Code Duplication
				else {
591
					$bFoundFunction = false;
592
					$objResponse = new xajaxResponse();
593
					$objResponse->addAlert("Unknown Function $sFunctionName.");
594
					$sResponse = $objResponse->getXML();
595
				}
596
			}
597 View Code Duplication
			else if ($this->aFunctionRequestTypes[$sFunctionName] != $requestMode)
598
			{
599
				$bFoundFunction = false;
600
				$objResponse = new xajaxResponse();
601
				$objResponse->addAlert("Incorrect Request Type.");
602
				$sResponse = $objResponse->getXML();
603
			}
604
		}
605
606
		if ($bFoundFunction)
607
		{
608
			for ($i = 0; $i < sizeof($aArgs); $i++)
609
			{
610
				// If magic quotes is on, then we need to strip the slashes from the args
611
				if (get_magic_quotes_gpc() == 1 && is_string($aArgs[$i])) {
612
613
					$aArgs[$i] = stripslashes($aArgs[$i]);
614
				}
615
				if (stristr($aArgs[$i],"<xjxobj>") != false)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stristr($aArgs[$i], '<xjxobj>') of type string to the boolean false. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
616
				{
617
					$aArgs[$i] = $this->_xmlToArray("xjxobj",$aArgs[$i]);
618
				}
619
				else if (stristr($aArgs[$i],"<xjxquery>") != false)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing stristr($aArgs[$i], '<xjxquery>') of type string to the boolean false. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
620
				{
621
					$aArgs[$i] = $this->_xmlToArray("xjxquery",$aArgs[$i]);
622
				}
623
				else if ($this->bDecodeUTF8Input)
624
				{
625
					$aArgs[$i] = $this->_decodeUTF8Data($aArgs[$i]);
626
				}
627
			}
628
629
			if ($this->sPreFunction) {
630
				$mPreResponse = $this->_callFunction($this->sPreFunction, array($sFunctionNameForSpecial, $aArgs));
631
				if (is_array($mPreResponse) && $mPreResponse[0] === false) {
632
					$bEndRequest = true;
633
					$sPreResponse = $mPreResponse[1];
634
				}
635
				else {
636
					$sPreResponse = $mPreResponse;
637
				}
638
				if (is_a($sPreResponse, "xajaxResponse")) {
639
					$sPreResponse = $sPreResponse->getXML();
640
				}
641
				if ($bEndRequest) $sResponse = $sPreResponse;
642
			}
643
644
			if (!$bEndRequest) {
645
				if (!$this->_isFunctionCallable($sFunctionName)) {
646
					$objResponse = new xajaxResponse();
647
					$objResponse->addAlert("The Registered Function $sFunctionName Could Not Be Found.");
648
					$sResponse = $objResponse->getXML();
649
				}
650
				else {
651
					if ($bFunctionIsCatchAll) {
652
						$aArgs = array($sFunctionNameForSpecial, $aArgs);
653
					}
654
					$sResponse = $this->_callFunction($sFunctionName, $aArgs);
655
				}
656
				if (is_a($sResponse, "xajaxResponse")) {
657
					$sResponse = $sResponse->getXML();
658
				}
659
				if (!is_string($sResponse) || strpos($sResponse, "<xjx>") === FALSE) {
660
					$objResponse = new xajaxResponse();
661
					$objResponse->addAlert("No XML Response Was Returned By Function $sFunctionName.");
662
					$sResponse = $objResponse->getXML();
663
				}
664
				else if ($sPreResponse != "") {
665
					$sNewResponse = new xajaxResponse($this->sEncoding, $this->bOutputEntities);
666
					$sNewResponse->loadXML($sPreResponse);
667
					$sNewResponse->loadXML($sResponse);
668
					$sResponse = $sNewResponse->getXML();
669
				}
670
			}
671
		}
672
673
		$sContentHeader = "Content-type: text/xml;";
674 View Code Duplication
		if ($this->sEncoding && strlen(trim($this->sEncoding)) > 0)
675
			$sContentHeader .= " charset=".$this->sEncoding;
676
		header($sContentHeader);
677
		if ($this->bErrorHandler && !empty( $GLOBALS['xajaxErrorHandlerText'] )) {
678
			$sErrorResponse = new xajaxResponse();
679
			$sErrorResponse->addAlert("** PHP Error Messages: **" . $GLOBALS['xajaxErrorHandlerText']);
680
			if ($this->sLogFile) {
681
				$fH = @fopen($this->sLogFile, "a");
682
				if (!$fH) {
683
					$sErrorResponse->addAlert("** Logging Error **\n\nxajax was unable to write to the error log file:\n" . $this->sLogFile);
684
				}
685
				else {
686
					fwrite($fH, "** xajax Error Log - " . strftime("%b %e %Y %I:%M:%S %p") . " **" . $GLOBALS['xajaxErrorHandlerText'] . "\n\n\n");
687
					fclose($fH);
688
				}
689
			}
690
691
			$sErrorResponse->loadXML($sResponse);
692
			$sResponse = $sErrorResponse->getXML();
693
694
		}
695
		if ($this->bCleanBuffer) while (@ob_end_clean());
696
		print $sResponse;
697
		if ($this->bErrorHandler) restore_error_handler();
698
699
		if ($this->bExitAllowed)
700
			exit();
701
	}
702
703
	/**
704
	 * Prints the xajax Javascript header and wrapper code into your page by
705
	 * printing the output of the getJavascript() method. It should only be
706
	 * called between the <pre><head> </head></pre> tags in your HTML page.
707
	 * Remember, if you only want to obtain the result of this function, use
708
	 * {@link xajax::getJavascript()} instead.
709
	 *
710
	 * <i>Usage:</i>
711
	 * <code>
712
	 *  <head>
713
	 *		...
714
	 *		< ?php $xajax->printJavascript(); ? >
715
	 * </code>
716
	 *
717
	 * @param string the relative address of the folder where xajax has been
718
	 *               installed. For instance, if your PHP file is
719
	 *               "http://www.myserver.com/myfolder/mypage.php"
720
	 *               and xajax was installed in
721
	 *               "http://www.myserver.com/anotherfolder", then $sJsURI
722
	 *               should be set to "../anotherfolder". Defaults to assuming
723
	 *               xajax is in the same folder as your PHP file.
724
	 * @param string the relative folder/file pair of the xajax Javascript
725
	 *               engine located within the xajax installation folder.
726
	 *               Defaults to xajax_js/xajax.js.
727
	 */
728
	function printJavascript($sJsURI="", $sJsFile=NULL)
729
	{
730
		print $this->getJavascript($sJsURI, $sJsFile);
731
	}
732
733
	/**
734
	 * Returns the xajax Javascript code that should be added to your HTML page
735
	 * between the <kbd><head> </head></kbd> tags.
736
	 *
737
	 * <i>Usage:</i>
738
	 * <code>
739
	 *  < ?php $xajaxJSHead = $xajax->getJavascript(); ? >
740
	 *	<head>
741
	 *		...
742
	 *		< ?php echo $xajaxJSHead; ? >
743
	 * </code>
744
	 *
745
	 * @param string the relative address of the folder where xajax has been
746
	 *               installed. For instance, if your PHP file is
747
	 *               "http://www.myserver.com/myfolder/mypage.php"
748
	 *               and xajax was installed in
749
	 *               "http://www.myserver.com/anotherfolder", then $sJsURI
750
	 *               should be set to "../anotherfolder". Defaults to assuming
751
	 *               xajax is in the same folder as your PHP file.
752
	 * @param string the relative folder/file pair of the xajax Javascript
753
	 *               engine located within the xajax installation folder.
754
	 *               Defaults to xajax_js/xajax.js.
755
	 * @return string
756
	 */
757
	function getJavascript($sJsURI="", $sJsFile=NULL)
758
	{
759
		$html = $this->getJavascriptConfig();
760
		$html .= $this->getJavascriptInclude($sJsURI, $sJsFile);
761
762
		return $html;
763
	}
764
765
	/**
766
	 * Returns a string containing inline Javascript that sets up the xajax
767
	 * runtime (typically called internally by xajax from get/printJavascript).
768
	 *
769
	 * @return string
770
	 */
771
	function getJavascriptConfig()
772
	{
773
		$html  = "\t<script type=\"text/javascript\">\n";
774
		$html .= "var xajaxRequestUri=\"".$this->sRequestURI."\";\n";
775
		$html .= "var xajaxDebug=".($this->bDebug?"true":"false").";\n";
776
		$html .= "var xajaxStatusMessages=".($this->bStatusMessages?"true":"false").";\n";
777
		$html .= "var xajaxWaitCursor=".($this->bWaitCursor?"true":"false").";\n";
778
		$html .= "var xajaxDefinedGet=".XAJAX_GET.";\n";
779
		$html .= "var xajaxDefinedPost=".XAJAX_POST.";\n";
780
		$html .= "var xajaxLoaded=false;\n";
781
782
		foreach($this->aFunctions as $sFunction => $bExists) {
783
			$html .= $this->_wrap($sFunction,$this->aFunctionRequestTypes[$sFunction]);
784
		}
785
786
		$html .= "\t</script>\n";
787
		return $html;
788
	}
789
790
	/**
791
	 * Returns a string containing a Javascript include of the xajax.js file
792
	 * along with a check to see if the file loaded after six seconds
793
	 * (typically called internally by xajax from get/printJavascript).
794
	 *
795
	 * @param string the relative address of the folder where xajax has been
796
	 *               installed. For instance, if your PHP file is
797
	 *               "http://www.myserver.com/myfolder/mypage.php"
798
	 *               and xajax was installed in
799
	 *               "http://www.myserver.com/anotherfolder", then $sJsURI
800
	 *               should be set to "../anotherfolder". Defaults to assuming
801
	 *               xajax is in the same folder as your PHP file.
802
	 * @param string the relative folder/file pair of the xajax Javascript
803
	 *               engine located within the xajax installation folder.
804
	 *               Defaults to xajax_js/xajax.js.
805
	 * @return string
806
	 */
807
	function getJavascriptInclude($sJsURI="", $sJsFile=NULL)
808
	{
809
		if ($sJsFile == NULL) $sJsFile = "xajax_js/xajax.js";
810
811
		if ($sJsURI != "" && substr($sJsURI, -1) != "/") $sJsURI .= "/";
812
813
		$html = "\t<script type=\"text/javascript\" src=\"" . $sJsURI . $sJsFile . "\"></script>\n";
814
		$html .= "\t<script type=\"text/javascript\">\n";
815
		$html .= "window.setTimeout(function () { if (!xajaxLoaded) { alert('Error: the xajax Javascript file could not be included. Perhaps the URL is incorrect?\\nURL: {$sJsURI}{$sJsFile}'); } }, 6000);\n";
816
		$html .= "\t</script>\n";
817
		return $html;
818
	}
819
820
	/**
821
	 * This method can be used to create a new xajax.js file out of the
822
	 * xajax_uncompressed.js file (which will only happen if xajax.js doesn't
823
	 * already exist on the filesystem).
824
	 *
825
	 * @param string an optional argument containing the full server file path
826
	 *               of xajax.js.
827
	 */
828
	function autoCompressJavascript($sJsFullFilename=NULL)
829
	{
830
		$sJsFile = "xajax_js/xajax.js";
831
832
		if ($sJsFullFilename) {
833
			$realJsFile = $sJsFullFilename;
834
		}
835
		else {
836
			$realPath = realpath(dirname(__FILE__));
837
			$realJsFile = $realPath . "/". $sJsFile;
838
		}
839
840
		// Create a compressed file if necessary
841
		if (!file_exists($realJsFile)) {
842
			$srcFile = str_replace(".js", "_uncompressed.js", $realJsFile);
843
			if (!file_exists($srcFile)) {
844
				trigger_error("The xajax uncompressed Javascript file could not be found in the <b>" . dirname($realJsFile) . "</b> folder. Error ", E_USER_ERROR);
845
			}
846
			require(dirname(__FILE__)."/xajaxCompress.php");
847
			$javaScript = implode('', file($srcFile));
848
			$compressedScript = xajaxCompressJavascript($javaScript);
849
			$fH = @fopen($realJsFile, "w");
850
			if (!$fH) {
851
				trigger_error("The xajax compressed javascript file could not be written in the <b>" . dirname($realJsFile) . "</b> folder. Error ", E_USER_ERROR);
852
			}
853
			else {
854
				fwrite($fH, $compressedScript);
855
				fclose($fH);
856
			}
857
		}
858
	}
859
860
	/**
861
	 * Returns the current URL based upon the SERVER vars.
862
	 *
863
	 * @access private
864
	 * @return string
865
	 */
866
	function _detectURI() {
867
		$aURL = array();
868
869
		// Try to get the request URL
870
		if (!empty($_SERVER['REQUEST_URI'])) {
871
			$aURL = parse_url($_SERVER['REQUEST_URI']);
872
		}
873
874
		// Fill in the empty values
875
		if (empty($aURL['scheme'])) {
876
			if (!empty($_SERVER['HTTP_SCHEME'])) {
877
				$aURL['scheme'] = $_SERVER['HTTP_SCHEME'];
878
			} else {
879
				$aURL['scheme'] = (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off') ? 'https' : 'http';
880
			}
881
		}
882
883
		if (empty($aURL['host'])) {
884
			if (!empty($_SERVER['HTTP_HOST'])) {
885
				if (strpos($_SERVER['HTTP_HOST'], ':') > 0) {
886
					list($aURL['host'], $aURL['port']) = explode(':', $_SERVER['HTTP_HOST']);
887
				} else {
888
					$aURL['host'] = $_SERVER['HTTP_HOST'];
889
				}
890
			} else if (!empty($_SERVER['SERVER_NAME'])) {
891
				$aURL['host'] = $_SERVER['SERVER_NAME'];
892
			} else {
893
				print "xajax Error: xajax failed to automatically identify your Request URI.";
894
				print "Please set the Request URI explicitly when you instantiate the xajax object.";
895
				exit();
896
			}
897
		}
898
899
		if (empty($aURL['port']) && !empty($_SERVER['SERVER_PORT'])) {
900
			$aURL['port'] = $_SERVER['SERVER_PORT'];
901
		}
902
903
		if (empty($aURL['path'])) {
904
			if (!empty($_SERVER['PATH_INFO'])) {
905
				$sPath = parse_url($_SERVER['PATH_INFO']);
906
			} else {
907
				$sPath = parse_url(api_get_self());
908
			}
909
			$aURL['path'] = $sPath['path'];
910
			unset($sPath);
911
		}
912
913
		if (!empty($aURL['query'])) {
914
			$aURL['query'] = '?'.$aURL['query'];
915
		}
916
917
		// Build the URL: Start with scheme, user and pass
918
		$sURL = $aURL['scheme'].'://';
919
		if (!empty($aURL['user'])) {
920
			$sURL.= $aURL['user'];
921
			if (!empty($aURL['pass'])) {
922
				$sURL.= ':'.$aURL['pass'];
923
			}
924
			$sURL.= '@';
925
		}
926
927
		// Add the host
928
		$sURL.= $aURL['host'];
929
930
		// Add the port if needed
931
		if (!empty($aURL['port']) && (($aURL['scheme'] == 'http' && $aURL['port'] != 80) || ($aURL['scheme'] == 'https' && $aURL['port'] != 443))) {
932
			$sURL.= ':'.$aURL['port'];
933
		}
934
935
		// Add the path and the query string
936
		$sURL.= $aURL['path'].@$aURL['query'];
937
938
		// Clean up
939
		unset($aURL);
940
		return $sURL;
941
	}
942
943
	/**
944
	 * Returns true if the function name is associated with an object callback,
945
	 * false if not.
946
	 *
947
	 * @param string the name of the function
948
	 * @access private
949
	 * @return boolean
950
	 */
951
	function _isObjectCallback($sFunction)
952
	{
953
		if (array_key_exists($sFunction, $this->aObjects)) return true;
954
		return false;
955
	}
956
957
	/**
958
	 * Returns true if the function or object callback can be called, false if
959
	 * not.
960
	 *
961
	 * @param string the name of the function
962
	 * @access private
963
	 * @return boolean
964
	 */
965
	function _isFunctionCallable($sFunction)
966
	{
967
		if ($this->_isObjectCallback($sFunction)) {
968
			if (is_object($this->aObjects[$sFunction][0])) {
969
				return method_exists($this->aObjects[$sFunction][0], $this->aObjects[$sFunction][1]);
970
			}
971
			else {
972
				return is_callable($this->aObjects[$sFunction]);
973
			}
974
		}
975
		else {
976
			return function_exists($sFunction);
977
		}
978
	}
979
980
	/**
981
	 * Calls the function, class method, or object method with the supplied
982
	 * arguments.
983
	 *
984
	 * @param string the name of the function
985
	 * @param array  arguments to pass to the function
986
	 * @access private
987
	 * @return mixed the output of the called function or method
988
	 */
989
	function _callFunction($sFunction, $aArgs)
990
	{
991
		if ($this->_isObjectCallback($sFunction)) {
992
			$mReturn = call_user_func_array($this->aObjects[$sFunction], $aArgs);
993
		}
994
		else {
995
			$mReturn = call_user_func_array($sFunction, $aArgs);
996
		}
997
		return $mReturn;
998
	}
999
1000
	/**
1001
	 * Generates the Javascript wrapper for the specified PHP function.
1002
	 *
1003
	 * @param string the name of the function
1004
	 * @param mixed  the request type
1005
	 * @access private
1006
	 * @return string
1007
	 */
1008
	function _wrap($sFunction,$sRequestType=XAJAX_POST)
1009
	{
1010
		$js = "function ".$this->sWrapperPrefix."$sFunction(){return xajax.call(\"$sFunction\", arguments, ".$sRequestType.");}\n";
1011
		return $js;
1012
	}
1013
1014
	/**
1015
	 * Takes a string containing xajax xjxobj XML or xjxquery XML and builds an
1016
	 * array representation of it to pass as an argument to the PHP function
1017
	 * being called.
1018
	 *
1019
	 * @param string the root tag of the XML
1020
	 * @param string XML to convert
1021
	 * @access private
1022
	 * @return array
1023
	 */
1024
	function _xmlToArray($rootTag, $sXml)
1025
	{
1026
		$aArray = array();
1027
		$sXml = str_replace("<$rootTag>","<$rootTag>|~|",$sXml);
1028
		$sXml = str_replace("</$rootTag>","</$rootTag>|~|",$sXml);
1029
		$sXml = str_replace("<e>","<e>|~|",$sXml);
1030
		$sXml = str_replace("</e>","</e>|~|",$sXml);
1031
		$sXml = str_replace("<k>","<k>|~|",$sXml);
1032
		$sXml = str_replace("</k>","|~|</k>|~|",$sXml);
1033
		$sXml = str_replace("<v>","<v>|~|",$sXml);
1034
		$sXml = str_replace("</v>","|~|</v>|~|",$sXml);
1035
		$sXml = str_replace("<q>","<q>|~|",$sXml);
1036
		$sXml = str_replace("</q>","|~|</q>|~|",$sXml);
1037
1038
		$this->aObjArray = explode("|~|",$sXml);
1039
1040
		$this->iPos = 0;
1041
		$aArray = $this->_parseObjXml($rootTag);
1042
1043
		return $aArray;
1044
	}
1045
1046
	/**
1047
	 * A recursive function that generates an array from the contents of
1048
	 * $this->aObjArray.
1049
	 *
1050
	 * @param string the root tag of the XML
1051
	 * @access private
1052
	 * @return array
1053
	 */
1054
	function _parseObjXml($rootTag)
1055
	{
1056
		$aArray = array();
1057
1058
		if ($rootTag == "xjxobj")
1059
		{
1060
			while(!stristr($this->aObjArray[$this->iPos],"</xjxobj>"))
1061
			{
1062
				$this->iPos++;
1063
				if(stristr($this->aObjArray[$this->iPos],"<e>"))
1064
				{
1065
					$key = "";
1066
					$value = null;
1067
1068
					$this->iPos++;
1069
					while(!stristr($this->aObjArray[$this->iPos],"</e>"))
1070
					{
1071
						if(stristr($this->aObjArray[$this->iPos],"<k>"))
1072
						{
1073
							$this->iPos++;
1074
							while(!stristr($this->aObjArray[$this->iPos],"</k>"))
1075
							{
1076
								$key .= $this->aObjArray[$this->iPos];
1077
								$this->iPos++;
1078
							}
1079
						}
1080
						if(stristr($this->aObjArray[$this->iPos],"<v>"))
1081
						{
1082
							$this->iPos++;
1083
							while(!stristr($this->aObjArray[$this->iPos],"</v>"))
1084
							{
1085
								if(stristr($this->aObjArray[$this->iPos],"<xjxobj>"))
1086
								{
1087
									$value = $this->_parseObjXml("xjxobj");
1088
									$this->iPos++;
1089
								}
1090
								else
1091
								{
1092
									$value .= $this->aObjArray[$this->iPos];
1093
									if ($this->bDecodeUTF8Input)
1094
									{
1095
										$value = $this->_decodeUTF8Data($value);
1096
									}
1097
								}
1098
								$this->iPos++;
1099
							}
1100
						}
1101
						$this->iPos++;
1102
					}
1103
1104
					$aArray[$key]=$value;
1105
				}
1106
			}
1107
		}
1108
1109
		if ($rootTag == "xjxquery")
1110
		{
1111
			$sQuery = "";
1112
			$this->iPos++;
1113
			while(!stristr($this->aObjArray[$this->iPos],"</xjxquery>"))
1114
			{
1115
				if (stristr($this->aObjArray[$this->iPos],"<q>") || stristr($this->aObjArray[$this->iPos],"</q>"))
1116
				{
1117
					$this->iPos++;
1118
					continue;
1119
				}
1120
				$sQuery	.= $this->aObjArray[$this->iPos];
1121
				$this->iPos++;
1122
			}
1123
1124
			parse_str($sQuery, $aArray);
1125
			if ($this->bDecodeUTF8Input)
1126
			{
1127
				foreach($aArray as $key => $value)
0 ignored issues
show
Bug introduced by
The expression $aArray of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1128
				{
1129
					$aArray[$key] = $this->_decodeUTF8Data($value);
1130
				}
1131
			}
1132
			// If magic quotes is on, then we need to strip the slashes from the
1133
			// array values because of the parse_str pass which adds slashes
1134
			if (get_magic_quotes_gpc() == 1) {
1135
				$newArray = array();
1136
				foreach ($aArray as $sKey => $sValue) {
0 ignored issues
show
Bug introduced by
The expression $aArray of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1137
					if (is_string($sValue))
1138
						$newArray[$sKey] = stripslashes($sValue);
1139
					else
1140
						$newArray[$sKey] = $sValue;
1141
				}
1142
				$aArray = $newArray;
1143
			}
1144
		}
1145
1146
		return $aArray;
1147
	}
1148
1149
	/**
1150
	 * Decodes string data from UTF-8 to the current xajax encoding.
1151
	 *
1152
	 * @param string data to convert
1153
	 * @access private
1154
	 * @return string converted data
1155
	 */
1156
	function _decodeUTF8Data($sData)
1157
	{
1158
		$sValue = $sData;
1159
		if ($this->bDecodeUTF8Input)
1160
		{
1161
			$sFuncToUse = NULL;
1162
1163
			// An adaptation for the Dokeos LMS, 22-AUG-2009.
1164
			if (function_exists('api_convert_encoding'))
1165
			{
1166
				$sFuncToUse = "api_convert_encoding";
1167
			}
1168
			//if (function_exists('iconv'))
1169
			elseif (function_exists('iconv'))
1170
			//
1171
			{
1172
				$sFuncToUse = "iconv";
1173
			}
1174
			else if (function_exists('mb_convert_encoding'))
1175
			{
1176
				$sFuncToUse = "mb_convert_encoding";
1177
			}
1178
			else if ($this->sEncoding == "ISO-8859-1")
1179
			{
1180
				$sFuncToUse = "utf8_decode";
1181
			}
1182
			else
1183
			{
1184
				trigger_error("The incoming xajax data could not be converted from UTF-8", E_USER_NOTICE);
1185
			}
1186
1187
			if ($sFuncToUse)
1188
			{
1189
				if (is_string($sValue))
1190
				{
1191
					if ($sFuncToUse == "iconv")
1192
					{
1193
						$sValue = iconv("UTF-8", $this->sEncoding.'//TRANSLIT', $sValue);
1194
					}
1195
					else if ($sFuncToUse == "mb_convert_encoding")
1196
					{
1197
						$sValue = mb_convert_encoding($sValue, $this->sEncoding, "UTF-8");
1198
					}
1199
					// Added code, an adaptation for the Dokeos LMS, 22-AUG-2009.
1200
					else if ($sFuncToUse == "api_convert_encoding")
1201
					{
1202
						$sValue = api_convert_encoding($sValue, $this->sEncoding, "UTF-8");
1203
					}
1204
					//
1205
					else
1206
					{
1207
						$sValue = utf8_decode($sValue);
1208
					}
1209
				}
1210
			}
1211
		}
1212
		return $sValue;
1213
	}
1214
1215
}// end class xajax
1216
1217
/**
1218
 * This function is registered with PHP's set_error_handler() function if
1219
 * the xajax error handling system is turned on.
1220
 */
1221
function xajaxErrorHandler($errno, $errstr, $errfile, $errline)
1222
{
1223
	$errorReporting = error_reporting();
1224
	if (($errno & $errorReporting) == 0) return;
1225
1226
	if ($errno == E_NOTICE) {
1227
		$errTypeStr = "NOTICE";
1228
	}
1229
	else if ($errno == E_WARNING) {
1230
		$errTypeStr = "WARNING";
1231
	}
1232
	else if ($errno == E_USER_NOTICE) {
1233
		$errTypeStr = "USER NOTICE";
1234
	}
1235
	else if ($errno == E_USER_WARNING) {
1236
		$errTypeStr = "USER WARNING";
1237
	}
1238
	else if ($errno == E_USER_ERROR) {
1239
		$errTypeStr = "USER FATAL ERROR";
1240
	}
1241
	else if ($errno == E_STRICT) {
1242
		return;
1243
	}
1244
	else {
1245
		$errTypeStr = "UNKNOWN: $errno";
1246
	}
1247
	$GLOBALS['xajaxErrorHandlerText'] .= "\n----\n[$errTypeStr] $errstr\nerror in line $errline of file $errfile";
1248
}
1249
1250
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
1251