Test Failed
Push — master ( 647c72...cd42b5 )
by
unknown
10:25
created

FileLoader::printFiles()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 5
nop 4
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Manager for including JS and CSS files into the desired order.
5
 */
6
class FileLoader {
7
8
	private $source;
9
10
	public function __construct()
11
	{
12
		// Unique cache file per grommunio Web location.
13
		$basePath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . '.' . md5(realpath(__FILE__));
14
		$this->cacheFile = "$basePath-loadcache";
0 ignored issues
show
Bug Best Practice introduced by
The property cacheFile does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
15
		$this->cacheSum = "$basePath-loadsum";
0 ignored issues
show
Bug Best Practice introduced by
The property cacheSum does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
16
       		$this->source = DEBUG_LOADER === LOAD_SOURCE;
17
	}
18
19
	/**
20
	 * Obtain the list of Extjs & UX files
21
	 *
22
	 * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag
23
	 * to indicate which files should be loaded.
24
	 * @return array The array of Javascript files
25
	 */
26
	public function getExtjsJavascriptFiles($load)
27
	{
28
		$jsLoadingSequence = array();
29
30
		if ($load == LOAD_RELEASE) {
31
			$jsLoadingSequence[] = "client/extjs/ext-base-all.js";
32
			$jsLoadingSequence[] = "client/extjs/ux/ux-all.js";
33
			$jsLoadingSequence[] = "client/extjs-mod/extjs-mod.js";
34
			$jsLoadingSequence[] = "client/tinymce/tinymce.min.js";
35
			$jsLoadingSequence[] = "client/third-party/ux-thirdparty.js";
36
			$jsLoadingSequence[] = "client/dompurify/purify.js";
37
		} else if ($load == LOAD_DEBUG) {
38
			$jsLoadingSequence[] = "client/extjs/ext-base-debug.js";
39
			$jsLoadingSequence[] = "client/extjs/ext-all-debug.js";
40
			$jsLoadingSequence[] = "client/extjs/ux/ux-all-debug.js";
41
			$jsLoadingSequence[] = "client/extjs-mod/extjs-mod-debug.js";
42
			$jsLoadingSequence[] = "client/tinymce/tinymce.js";
43
			$jsLoadingSequence[] = "client/third-party/ux-thirdparty-debug.js";
44
			$jsLoadingSequence[] = "client/dompurify/purify.js";
45
		} else {
46
			$jsLoadingSequence[] = "client/extjs/ext-base-debug.js";
47
			$jsLoadingSequence[] = "client/extjs/ext-all-debug.js";
48
			$jsLoadingSequence[] = "client/extjs/ux/ux-all-debug.js";
49
			$jsLoadingSequence = array_merge(
50
				$jsLoadingSequence,
51
				$this->buildJSLoadingSequence(
52
					$this->getListOfFiles('js', 'client/extjs-mod')
53
				)
54
			);
55
			$jsLoadingSequence[] = "client/tinymce/tinymce.js";
56
			$jsLoadingSequence[] = "client/dompurify/purify.js";
57
			$jsLoadingSequence = array_merge(
58
				$jsLoadingSequence,
59
				$this->buildJSLoadingSequence(
60
					$this->getListOfFiles('js', 'client/third-party')
61
				)
62
			);
63
		}
64
65
		return $jsLoadingSequence;
66
	}
67
68
	/**
69
	 * Obtain the list of Extjs & UX files
70
	 *
71
	 * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag
72
	 * to indicate which files should be loaded.
73
	 * @return array The array of CSS files
74
	 */
75
	public function getExtjsCSSFiles($load)
0 ignored issues
show
Unused Code introduced by
The parameter $load is not used and could be removed. ( Ignorable by Annotation )

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

75
	public function getExtjsCSSFiles(/** @scrutinizer ignore-unused */ $load)

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

Loading history...
76
	{
77
		return array("client/extjs/resources/css/ext-all-ux.css");
78
	}
79
80
	/**
81
	 * Obtain the list of grommunio Web files
82
	 *
83
	 * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag
84
	 * to indicate which files should be loaded.
85
	 * @param array $libFiles (optional) library files when $load = LOAD_SOURCE
86
	 * @return array The array of Javascript files
87
	 */
88
	public function getZarafaJavascriptFiles($load, $libFiles = Array())
89
	{
90
		$jsLoadingSequence = array();
91
92
		if ($load == LOAD_RELEASE) {
93
			$jsLoadingSequence[] = "client/grommunio.js";
94
		} else if ($load == LOAD_DEBUG) {
95
			$jsLoadingSequence[] = "client/grommunio-debug.js";
96
		} else {
97
			$jsLoadingSequence = array_merge(
98
				$jsLoadingSequence,
99
				$this->buildJSLoadingSequence(
100
					$this->getListOfFiles('js', 'client/zarafa'),
101
					Array('client/zarafa/core'),
102
					$libFiles
103
				)
104
			);
105
		}
106
107
		return $jsLoadingSequence;
108
	}
109
110
	/**
111
	 * Obtain the list of all Javascript files as registered by the plugins.
112
	 *
113
	 * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag
114
	 * to indicate which files should be loaded.
115
	 * @param array $libFiles (optional) library files when $load = LOAD_SOURCE
116
	 * @return array The array of Javascript files
117
	 */
118
	public function getPluginJavascriptFiles($load, $libFiles = Array())
119
	{
120
		if ($load === LOAD_SOURCE) {
121
			return $this->buildJSLoadingSequence(
122
				$GLOBALS['PluginManager']->getClientFiles($load),
123
				Array(),
124
				$libFiles
125
			);
126
		} else {
127
			return $GLOBALS['PluginManager']->getClientFiles($load);
128
		}
129
	}
130
131
	/**
132
	 * Obtain the list of all CSS files as registered by the plugins.
133
	 *
134
	 * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag
135
	 * to indicate which files should be loaded.
136
	 * @return array The array of CSS files
137
	 */
138
	public function getPluginCSSFiles($load)
139
	{
140
		return $GLOBALS['PluginManager']->getResourceFiles($load);
141
	}
142
143
	/**
144
	 * Obtain the list of all Javascript files as provided by plugins using PluginManager#triggerHook
145
	 * for the hook 'server.main.include.jsfiles'.
146
	 *
147
	 * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag
148
	 * to indicate which files should be loaded.
149
	 * @return array The array of Javascript files
150
	 */
151
	public function getRemoteJavascriptFiles($load)
152
	{
153
		$files = Array();
154
		$GLOBALS['PluginManager']->triggerHook('server.main.include.jsfiles', Array('load' => $load, 'files'=> & $files));
155
		return $files;
156
	}
157
158
	/**
159
	 * Obtain the list of all CSS files as provided by plugins using PluginManager#triggerHook
160
	 * for the hook 'server.main.include.cssfiles'.
161
	 *
162
	 * @param number $load The LOAD_RELEASE | LOAD_DEBUG | LOAD_SOURCE flag
163
	 * to indicate which files should be loaded.
164
	 * @return array The array of CSS files
165
	 */
166
	public function getRemoteCSSFiles($load)
167
	{
168
		$files = Array();
169
		$GLOBALS['PluginManager']->triggerHook('server.main.include.cssfiles', Array('load' => $load, 'files'=> & $files));
170
		return $files;
171
	}
172
173
	/**
174
	 * Print each file on a new line using the given $template
175
	 * @param Array $files The files to print
176
	 * @param String $template The template used to print each file, the string {file} will
177
	 * be replaced with the filename
178
	 * @param Boolean $base True if only the basename of the file must be printed
179
	 * @param Boolean $concatVersion True if concatenate unique webapp version
180
	 * with file name to avoid the caching issue.
181
	 */
182
	public function printFiles($files, $template = '{file}', $base = false, $concatVersion = true)
183
	{
184
		foreach($files as $file) {
185
			$file = $base === true ? basename($file) : $file;
186
			if($concatVersion) {
187
				$file = $file."?version=".$this->getVersion();
188
			}
189
			echo str_replace('{file}', $file, $template) . PHP_EOL;
190
		}
191
	}
192
193
	/**
194
	 * Return grommunio Web version.
195
	 * @return String returns grommunio Web version
196
	 */
197
	public function getVersion()
198
	{
199
		return trim(file_get_contents('version'));
200
	}
201
202
	/**
203
	 * getJavascriptFiles
204
	 *
205
	 * Scanning files and subdirectories that can be found within the supplied
206
	 * path and add all located Javascript files to a list.
207
	 * @param $path String Path of the directory to scan
208
	 * @param $recursive Boolean If set to true scans subdirectories as well
209
	 * @param $excludeFiles Array Optional Paths of files or directories that
210
	 *                                     are excluded from the search.
211
	 * @return Array List of arrays containing the paths to files that have to be included.
212
	 */
213
	public function getJavascriptFiles($path, $recursive = true, $excludeFiles = Array()) {
214
		return $this->getListOfFiles('js', $path, $recursive, $excludeFiles);
215
	}
216
217
	/**
218
	 * getCSSFiles
219
	 *
220
	 * Scanning files and subdirectories that can be found within the supplied
221
	 * path and add all located CSS files to a list.
222
	 * @param $path String Path of the directory to scan
223
	 * @param $recursive Boolean If set to true scans subdirectories as well
224
	 * @param $excludeFiles Array Optional Paths of files or directories that
225
	 *                                     are excluded from the search.
226
	 * @return Array List of arrays containing the paths to files that have to be included.
227
	 */
228
	public function getCSSFiles($path, $recursive = true, $excludeFiles = Array()) {
229
		return $this->getListOfFiles('css', $path, $recursive, $excludeFiles);
230
	}
231
232
	/**
233
	 * getListOfFiles
234
	 *
235
	 * Scanning files and subdirectories that can be found within the supplied
236
	 * path and add the files to a list.
237
	 * @param $ext The extension of files that are included ("js" or "css")
238
	 * @param $path String Path of the directory to scan
239
	 * @param $recursive Boolean If set to true scans subdirectories as well
240
	 * @param $excludeFiles Array Optional Paths of files or directories that
241
	 *                                     are excluded from the search.
242
	 * @return Array List of arrays containing the paths to files that have to be included.
243
	 */
244
	private function getListOfFiles($ext, $path, $recursive = true, $excludeFiles = Array()) {
245
		/*
246
		 * We are using two lists of files to make sure the files from the
247
		 * subdirectories are added after the current directory files.
248
		 */
249
		$files = Array();
250
		$subDirFiles = Array();
251
252
		$dir = opendir($path);
253
		if (!is_resource($dir)) {
254
			return $files;
255
		}
256
257
		while(($file = readdir($dir)) !== false)
258
		{
259
			$filepath = $path.'/'.$file;
260
			// Skip entries like ".", ".." and ".svn"
261
			if (substr($file,0,1)!="." && !in_array($filepath, $excludeFiles)){
262
				// Make sure we have files to include
263
				$info = pathinfo($filepath, PATHINFO_EXTENSION);
264
265
				if(is_file($filepath) && $info == $ext){
266
					$files[] = $filepath;
267
				// Subdirectories will be scanned as well
268
				} elseif($recursive && is_dir($filepath)){
269
					$subDirFiles = array_merge($subDirFiles, $this->getListOfFiles($ext, $filepath, $recursive, $excludeFiles));
270
				}
271
			}
272
		}
273
274
		/*
275
		 * Make the lists alphabetically sorted, doing this separate makes sure
276
		 * the subdirectories are added after the files in the directory above.
277
		 */
278
		sort($files);
279
		sort($subDirFiles);
280
		$files = array_merge($files, $subDirFiles);
281
282
		return $files;
283
	}
284
285
	/**
286
	 * buildJSLoadingSequence
287
	 *
288
	 * Will build the correct loading sequence for the JS files in application based on the class,
289
	 * extends and depends statements in the files itself. It will first extract the class
290
	 * definitions and dependencies. It will put that information in a list that holds the
291
	 * dependencies for each file. With that list the proper sequence of loading can be constructed.
292
	 * Files that originate from any of the specified coreFiles folders will be marked as core files.
293
	 * @param $files Array List of files that have to be included.
294
	 * @param $coreFiles Array (Optional) List of folders that contain core files.
295
	 * @param $libFiles Array (Optional) List of files that is used as library (and can contain
296
	 * classed which are depended upon by the given files).
297
	 * @return Array List of files that are sorted in the correct sequence
298
	 */
299
	private function buildJSLoadingSequence($files, $coreFiles = Array(), $libFiles = Array()){
300
		// Create a lookup table to easily get the classes which are defined in the library files
301
		$libFileLookup = Array();
302
		// Create a lookup table to easily get the name of the file the class is defined in
303
		$classFileLookup = Array();
304
305
		$fileDataLookup = Array();
306
		$fileDependencies = Array();
307
308
		// Read all library files to determine the classes which are defined
309
		for ($i = 0, $len = count($libFiles); $i < $len; $i++) {
310
			$filename = $libFiles[$i];
311
			$content = $this->getFileContents($filename);
312
313
			$class = Array();
314
			preg_match_all('(@class\W([^\n\r]*))', $content, $class);
315
316
			$libFileLookup[ $filename ] = Array(
317
				'class' => $class[1]
318
			);
319
320
			for ($j = 0, $lenJ = count($class[1]); $j < $lenJ; $j++){
321
				$libFileLookup[ $class[1][$j] ] = true;
322
			}
323
		}
324
325
		for ($i = 0, $len = count($files); $i < $len; $i++) {
326
			$content = $this->getFileContents($files[$i]);
327
			$filename = $files[$i];
328
329
			$extends = Array();
330
			$dependsFile = Array();
331
			$class = Array();
332
333
			preg_match_all('(@extends\W([^\n\r]*))', $content, $extends);
334
			preg_match_all('(@class\W([^\n\r]*))', $content, $class);
335
			preg_match_all('(#dependsFile\W([^\n\r\*]+))', $content, $dependsFile);
336
			$core = (strpos($content, '#core') !== false)? true : false;
337
338
			for ($j = 0, $lenJ = count($coreFiles); $j < $lenJ; $j++){
339
				if(strpos($filename, $coreFiles[$j]) === 0){
340
					$core = true;
341
					break;
342
				}
343
			}
344
345
			$fileDataLookup[ $filename ] = Array(
346
				'class' => $class[1],
347
				'extends' => $extends[1],
348
				'dependsFile' => $dependsFile[1]
349
			);
350
			$fileDependencies[ $filename ] = Array(
351
				'depends' => Array(),
352
				'core' => $core		// Based on tag or on class or on file path?
353
			);
354
355
			for ($j = 0, $lenJ = count($class[1]); $j < $lenJ; $j++){
356
				$classFileLookup[ $class[1][$j] ] = $filename;
357
			}
358
		}
359
360
		// Convert dependencies found by searching for @extends to a filename.
361
		foreach ($fileDataLookup as $filename => &$fileData) {
362
363
			// First get the extended class dependencies. We also have to convert them into files names using the $classFileLookup.
364
			for ($i = 0, $len = count($fileData['extends']); $i < $len; $i++) {
365
				// The check if it extends the Zarafa namespace is needed because we do not index other namespaces.
366
				if(substr($fileData['extends'][$i], 0, strlen('Zarafa')) == 'Zarafa'){
367
					if (isset($libFileLookup[ $fileData['extends'][$i] ])) {
368
						// The @extends is found in the library file.
369
						// No need to update the dependencies
370
					} else if(isset($classFileLookup[ $fileData['extends'][$i] ])){
371
						// The @extends is found as @class in another file
372
						// Convert the class dependency into a filename
373
						$dependencyFilename = $classFileLookup[ $fileData['extends'][$i] ];
374
						// Make sure the file does not depend on itself
375
						if($dependencyFilename != $filename){
376
							$fileDependencies[ $filename ]['depends'][] = $dependencyFilename;
377
						}
378
					} else {
379
						trigger_error('Unable to find @extends dependency "'.$fileData['extends'][$i].'" for file "'.$filename.'"');
380
					}
381
				}
382
			}
383
384
			// Add the file dependencies that have beed added by using #dependsFile in the file.
385
			for ($i = 0, $len = count($fileData['dependsFile']); $i < $len; $i++){
386
				$dependencyFilename = $fileData['dependsFile'][$i];
387
				// Check if the file exists to prevent non-existent dependencies
388
				if(isset($fileDataLookup[ $dependencyFilename ])){
389
					// Make sure the file does not depend on itself
390
					if($dependencyFilename != $filename){
391
						$fileDependencies[ $filename ]['depends'][] = $dependencyFilename;
392
					}
393
				} else {
394
					trigger_error('Unable to find file #dependsFile dependency "'.$fileData['dependsFile'][$i].'" for file "'.$filename.'"');
395
				}
396
			}
397
		}
398
		unset($fileData);
399
400
		$fileSequence = $this->generateDependencyBasedFileSeq($fileDependencies);
401
402
		return $fileSequence;
403
	}
404
405
	/**
406
	 * generateDependencyBasedFileSeq
407
	 *
408
	 * This function will generate a loading sequence for the supplied list of files and their
409
	 * dependencies. This function calculates the depth of each file in the dependencytree. Based on
410
	 * that depth it calculates a weight for each file and that will determine the order in which
411
	 * the files will be included.
412
	 * The weight consists of two times the depth of the node and a penalty for files that have not
413
	 * been marked as a core file. This way core files get included prior to other files at the same
414
	 * depth. Files with the same weight are added in the order they are in the list and that should
415
	 * be alphabetically.
416
	 *
417
	 * @param $fileData Array List of files with dependency data in the format of
418
	 *                        $fileData[ FILENAME ] = Array(
419
	 *                          'depends' => Array(FILENAME1, FILENAME2),
420
	 *                          'core' => true|false
421
	 *                        );
422
	 * @return Array List of filenames in the calculated loading sequence
423
	 */
424
	private function generateDependencyBasedFileSeq($fileData){
425
		$fileDepths = Array();
426
427
		$changed = true;
428
		while($changed && (count($fileDepths) < count($fileData)) ){
429
			$changed = false;
430
431
			// Loop through all the files and see if for each file we can get a depth assigned based on their parents depth.
432
			foreach($fileData as $file => $dependencyData){
433
				$dependencies = $dependencyData['depends'];
434
435
				if(!isset($fileDepths[ $file ])){
436
					if(count($dependencies) > 0){
437
						$parentsDepthAssigned = true;
438
						$highestParentDepth = 0;
439
						// See if all the parents already have a depth assigned and if so take the highest one.
440
						for($i=0;$i<count($dependencies);$i++){
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
441
							// Not all parents depths have been assigned yet, wait another turn
442
							if(!isset($fileDepths[ $dependencies[$i] ])){
443
								$parentsDepthAssigned = false;
444
								break;
445
							} else {
446
								// We should only take the highest depth
447
								$highestParentDepth = max($highestParentDepth, $fileDepths[ $dependencies[$i] ]);
448
							}
449
						}
450
						// All parents have a depth assigned, we can calculate the one for this node.
451
						if($parentsDepthAssigned){
452
							$fileDepths[ $file ] = $highestParentDepth + 1;
453
							$changed = true;
454
						}
455
					// The node does not have any dependencies so its a root node.
456
					} else {
457
						$fileDepths[ $file ] = 0;
458
						$changed = true;
459
					}
460
				}
461
			}
462
		}
463
464
		// If not all the files have been assigned a depth, but nothing changed the last round there
465
		// must be something wrong with the dependencies of the skipped files. So lets tell someone.
466
		if(count($fileDepths) < count($fileData)){
467
			$errorMsg = '[LOADER] Could not compute all dependencies. The following files cannot be resolved properly: ';
468
			$errorMsg .= implode(', ', array_diff(array_keys($fileData), array_keys($fileDepths)));
469
			trigger_error($errorMsg);
470
		}
471
472
		$fileWeights = Array();
473
		// Now lets determine each file's weight
474
		foreach($fileData as $file => $dependencyData){
475
			if($fileDepths[ $file ] !== null){
476
				$weight = $fileDepths[ $file ] * 2;
477
				// Add a penalty of 1 to non-core files to up the core-files in the sequence.
478
				if(!$dependencyData['core']){
479
					$weight++;
480
				}
481
			} else {
482
				// Make up a weight to put it at the end
483
				$weight = count($fileData);
484
			}
485
			if(!isset($fileWeights[ $weight]))
486
				$fileWeights[ $weight ] = Array();
487
			$fileWeights[ $weight ][] = $file;
488
		}
489
490
		// The weights have not been added in the correct order, so sort it first on the keys.
491
		ksort($fileWeights);
492
493
		// Now put it all in the correct order. Files with the same weight are added in the order
494
		// they are in the list. This order should still be alphabetically.
495
		$fileSequence = Array();
496
		foreach($fileWeights as $weight => $fileList){
497
			for($i=0;$i<count($fileList);$i++){
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
498
				$fileSequence[] = $fileList[$i];
499
			}
500
		}
501
502
		return $fileSequence;
503
	}
504
505
	/**
506
	 * getFileContents
507
	 *
508
	 * Returns the content of the supplied file name.
509
	 * @param $fn String File name
510
	 * @return String Content of the file
511
	 */
512
	private function getFileContents($fn){
513
		$fn = strtok($fn, '?');
514
515
		$fc="";
516
		$fh = fopen($fn, "r");
517
		if($fh) {
0 ignored issues
show
introduced by
$fh is of type resource, thus it always evaluated to false.
Loading history...
518
			while(!feof($fh)){
519
				$fc .= fgets($fh, 4096);
520
			}
521
			fclose($fh);
522
		}
523
		return $fc;
524
	}
525
526
	/**
527
	 * The JavaScript load order for grommunio Web. The loader order is cached when grommunio Web
528
	 * is LOAD_SOURCE mode, since calculating the loader is quite expensive.
529
	 */
530
	public function jsOrder()
531
	{
532
		if ($this->source) {
533
			if ($this->cacheExists()) {
534
				echo file_get_contents($this->cacheFile);
535
				return;
536
			}
537
			ob_start();
538
		}
539
540
		list($extjsFiles, $webappFiles, $pluginFiles, $remoteFiles) = $this->getJsFiles();
541
542
		$jsTemplate = "\t\t<script type=\"text/javascript\" src=\"{file}\"></script>";
543
		$this->printFiles($extjsFiles, $jsTemplate);
544
		$this->printFiles($webappFiles, $jsTemplate);
545
		$this->printFiles($pluginFiles, $jsTemplate);
546
		$this->printFiles($remoteFiles, $jsTemplate);
547
548
		if ($this->source) {
549
			$contents = ob_get_contents();
550
			ob_end_clean();
551
			echo $contents;
552
			file_put_contents($this->cacheFile, $contents);
553
		}
554
	}
555
556
	/**
557
	 * Returns an array with all javascript files. The array has four entries, for the ExtJS files,
558
	 * the Zarafa files, the plugin files and the remote files respectively.
559
	 * This function will make sure that the directories are read only once.
560
	 *
561
	 * @return array An array that contains the names of all the javascript files that should be loaded
562
	 */
563
	private function getJsFiles() {
564
		if ( !isset($this->extjsfiles) ){
0 ignored issues
show
Bug introduced by
The property extjsfiles does not exist on FileLoader. Did you mean extjsFiles?
Loading history...
565
			$this->extjsFiles = $this->getExtjsJavascriptFiles(DEBUG_LOADER);
0 ignored issues
show
Bug Best Practice introduced by
The property extjsFiles does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
566
			$this->webappFiles = $this->getZarafaJavascriptFiles(DEBUG_LOADER, $this->extjsFiles);
0 ignored issues
show
Bug Best Practice introduced by
The property webappFiles does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
567
			$this->pluginFiles = $this->getPluginJavascriptFiles(DEBUG_LOADER, array_merge($this->extjsFiles, $this->webappFiles));
0 ignored issues
show
Bug Best Practice introduced by
The property pluginFiles does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
568
			$this->remoteFiles = $this->getRemoteJavascriptFiles(DEBUG_LOADER);
0 ignored issues
show
Bug Best Practice introduced by
The property remoteFiles does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
569
		}
570
571
		return array($this->extjsFiles, $this->webappFiles, $this->pluginFiles, $this->remoteFiles);
572
	}
573
574
	/**
575
	 * The CSS load order for grommunio Web
576
	 */
577
	public function cssOrder()
578
	{
579
		$cssTemplate = "\t\t<link rel=\"stylesheet\" type=\"text/css\" href=\"{file}\">";
580
		$extjsFiles = $this->getExtjsCSSFiles(DEBUG_LOADER);
581
		$this->printFiles($extjsFiles, $cssTemplate);
582
583
		// Since we only have one css file, we can add that directly
584
		$this->printFiles(array("client/resources/css/grommunio.css"), $cssTemplate);
585
586
		$pluginFiles = $this->getPluginCSSFiles(DEBUG_LOADER);
587
		$this->printFiles($pluginFiles, $cssTemplate);
588
589
		$remoteFiles = $this->getRemoteCSSFiles(DEBUG_LOADER);
590
		$this->printFiles($remoteFiles, $cssTemplate);
591
	}
592
593
	/**
594
	 * Checks if the JavaScript or CSS files on disk have been changed
595
	 * and writes a new md5 of the files to the disk.
596
	 *
597
	 * return boolean False if cache is outdated
598
	 */
599
	private function cacheExists()
600
	{
601
		list($extjsFiles, $webappFiles, $pluginFiles, $remoteFiles) = $this->getJsFiles();
602
		$files = [$extjsFiles, $webappFiles, $pluginFiles, $remoteFiles];
603
		$md5 = md5(json_encode($files));
604
605
		if (!file_exists($this->cacheSum) || file_get_contents($this->cacheSum) !== $md5) {
606
			file_put_contents($this->cacheSum, $md5);
607
			return false;
608
		}
609
610
		return true;
611
	}
612
}
613
?>
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...
614