1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7
8//this script may only be included - so its better to die if called directly.
9if (strpos($_SERVER["SCRIPT_NAME"], basename(__FILE__)) !== false) {
10	header("location: index.php");
11	exit;
12}
13
14/**
15 *
16 */
17class AdminLib extends TikiLib
18{
19
20	/**
21	 * @param $offset
22	 * @param $maxRecords
23	 * @param $sort_mode
24	 * @param $find
25	 * @return array
26	 */
27	function list_dsn($offset, $maxRecords, $sort_mode, $find)
28	{
29
30		$bindvars = [];
31		if ($find) {
32			$findesc = '%' . $find . '%';
33
34			$mid = " where (`dsn` like ?)";
35			$bindvars[] = $findesc;
36		} else {
37			$mid = "";
38		}
39
40		$query = "select * from `tiki_dsn` $mid order by " . $this->convertSortMode($sort_mode);
41		$query_cant = "select count(*) from `tiki_dsn` $mid";
42		$result = $this->query($query, $bindvars, $maxRecords, $offset);
43		$cant = $this->getOne($query_cant, $bindvars);
44		$ret = [];
45
46		while ($res = $result->fetchRow()) {
47			$ret[] = $res;
48		}
49
50		$retval = [];
51		$retval["data"] = $ret;
52		$retval["cant"] = $cant;
53		return $retval;
54	}
55
56	/**
57	 * @param $dsnId
58	 * @param $dsn
59	 * @param $name
60	 *
61	 * @return TikiDb_Pdo_Result|TikiDb_Adodb_Result
62	 */
63	function replace_dsn($dsnId, $dsn, $name)
64	{
65		// Check the name
66		if ($dsnId) {
67			$query = "update `tiki_dsn` set `name`=?,`dsn`=? where `dsnId`=?";
68			$bindvars = [$name, $dsn, $dsnId];
69			return $this->query($query, $bindvars);
70		} else {
71			$query = "delete from `tiki_dsn`where `name`=? and `dsn`=?";
72			$bindvars = [$name, $dsn];
73			$this->query($query, $bindvars);
74			$query = "insert into `tiki_dsn`(`name`,`dsn`)
75                		values(?,?)";
76			return $this->query($query, $bindvars);
77		}
78	}
79
80	/**
81	 * @param int $dsnId
82	 *
83	 * @return TikiDb_Pdo_Result|TikiDb_Adodb_Result
84	 */
85	function remove_dsn($dsnId)
86	{
87		$query = "delete from `tiki_dsn` where `dsnId`=?";
88		return $this->query($query, [$dsnId]);
89	}
90
91	/**
92	 * @param int $dsnId
93	 * @return array|bool returns false on failure, or an array of values upon success
94	 */
95	function get_dsn($dsnId)
96	{
97		$query = "select * from `tiki_dsn` where `dsnId`=?";
98
99		$result = $this->query($query, [$dsnId]);
100
101		if (! $result->numRows()) {
102			return false;
103		}
104
105		$res = $result->fetchRow();
106		return $res;
107	}
108
109	/**
110	 * @param $dsnName
111	 * @return array|bool returns false on failure, or an array of values upon success
112	 */
113	function get_dsn_from_name($dsnName)
114	{
115		$query = "select * from `tiki_dsn` where `name`=?";
116
117		$result = $this->query($query, [$dsnName]);
118
119		if (! $result->numRows()) {
120			return false;
121		}
122
123		$res = $result->fetchRow();
124		return $res;
125	}
126
127	/**
128	 * @param $offset
129	 * @param $maxRecords
130	 * @param $sort_mode
131	 * @param $find
132	 * @return array
133	 */
134	function list_extwiki($offset, $maxRecords, $sort_mode, $find)
135	{
136		$bindvars = [];
137		if ($find) {
138			$findesc = '%' . $find . '%';
139
140			$mid = " where (`extwiki` like ? )";
141			$bindvars[] = $findesc;
142		} else {
143			$mid = "";
144		}
145
146		$query = "select * from `tiki_extwiki` $mid order by " . $this->convertSortMode($sort_mode);
147		$query_cant = "select count(*) from `tiki_extwiki` $mid";
148		$result = $this->fetchAll($query, $bindvars, $maxRecords, $offset);
149		$cant = $this->getOne($query_cant, $bindvars);
150
151		$retval = [];
152		$retval["data"] = $result;
153		$retval["cant"] = $cant;
154		return $retval;
155	}
156
157	/**
158	 * @param int    $extwikiId
159	 * @param string $extwiki
160	 * @param        $name
161	 * @param string $indexName
162	 * @param array  $groups
163	 *
164	 * @return array|bool|mixed
165	 */
166	function replace_extwiki($extwikiId, $extwiki, $name, $indexName = '', $groups = [])
167	{
168		$table = $this->table('tiki_extwiki');
169		$data = [
170			'name' => $name,
171			'extwiki' => $extwiki,
172			'indexname' => $indexName,
173			'groups' => json_encode(array_values($groups)),
174		];
175		$withId = $data;
176		$withId['extwikiId'] = $extwikiId;
177		return $table->insertOrUpdate($withId, $data);
178	}
179
180	/**
181	 * Removes a configuration option of an external wiki
182	 *
183	 * @param $extwikiId int Id of the external wiki to be removed
184	 *
185	 * @return TikiDb_Pdo_Result|TikiDb_Adodb_Result
186	 */
187	function remove_extwiki($extwikiId)
188	{
189		$query = "delete from `tiki_extwiki` where `extwikiId`=?";
190		return $this->query($query, [$extwikiId]);
191	}
192
193	/**
194	 * @param int $extwikiId
195	 * @return bool
196	 */
197	function get_extwiki($extwikiId)
198	{
199		$table = $this->table('tiki_extwiki');
200		$row = $table->fetchFullRow(['extwikiId' => $extwikiId]);
201
202		if (! empty($row['groups'])) {
203			$row['groups'] = json_decode($row['groups']);
204		}
205		return $row;
206	}
207
208
209	/**
210	 * Remove unused wiki attachment pictures
211	 */
212	function remove_unused_pictures()
213	{
214		global $tikidomain;
215
216		$query = "select `data` from `tiki_pages`";
217		$result = $this->query($query, []);
218		$pictures = [];
219
220		while ($res = $result->fetchRow()) {
221			preg_match_all("/\{(picture |img )([^\}]+)\}/ixs", $res['data'], $pics); //fixme: pick also the picture into ~np~
222
223			foreach (array_unique($pics[2]) as $pic) {
224				if (preg_match("/(src|file)=\"([^\"]+)\"/xis", $pic, $matches)) {
225					$pictures[] = $matches[2];
226				}
227				if (preg_match("/(src|file)=&quot;([^&]+)&quot;/xis", $pic, $matches)) {
228					$pictures[] = $matches[2];
229				}
230				if (preg_match("/(src|file)=([^&\"\s,]+)/xis", $pic, $matches)) {
231					$pictures[] = $matches[2];
232				}
233			}
234		}
235		$pictures = array_unique($pictures);
236
237		$path = "img/wiki_up";
238		if ($tikidomain) {
239			$path .= "/$tikidomain";
240		}
241		$h = opendir($path);
242
243		while (($file = readdir($h)) !== false) {
244			if (is_file("$path/$file") && $file != 'license.txt' && $file != 'index.php' && $file != '.cvsignore' && $file != 'README') {
245				$filename = "$path/$file";
246
247				if (! in_array($filename, $pictures)) {
248					@unlink($filename);
249				}
250			}
251		}
252
253		closedir($h);
254	}
255
256
257	/**
258	 * An image gallery function that removes orphaned images.
259	 */
260
261	function remove_orphan_images()
262	{
263		$merge = [];
264
265		// Find images in tiki_pages
266		$query = "select `data` from `tiki_pages`";
267		$result = $this->query($query, []);
268
269		while ($res = $result->fetchRow()) {
270			preg_match_all("/src=\"([^\"]+)\"/", $res["data"], $reqs1);
271
272			preg_match_all("/src=\'([^\']+)\'/", $res["data"], $reqs2);
273			preg_match_all("/src=([A-Za-z0-9:\?\=\/\.\-\_]+)\}/", $res["data"], $reqs3);
274			$merge = array_merge($merge, $reqs1[1], $reqs2[1], $reqs3[1]);
275			$merge = array_unique($merge);
276		}
277
278		// Find images in Tiki articles
279		$query = "select `body` from `tiki_articles`";
280		$result = $this->query($query, []);
281
282		while ($res = $result->fetchRow()) {
283			preg_match_all("/src=\"([^\"]+)\"/", $res["body"], $reqs1);
284
285			preg_match_all("/src=\'([^\']+)\'/", $res["body"], $reqs2);
286			preg_match_all("/src=([A-Za-z0-9:\?\=\/\.\-\_]+)\}/", $res["body"], $reqs3);
287			$merge = array_merge($merge, $reqs1[1], $reqs2[1], $reqs3[1]);
288			$merge = array_unique($merge);
289		}
290
291		// Find images in tiki_submissions
292		$query = "select `body` from `tiki_submissions`";
293		$result = $this->query($query, []);
294
295		while ($res = $result->fetchRow()) {
296			preg_match_all("/src=\"([^\"]+)\"/", $res["body"], $reqs1);
297
298			preg_match_all("/src=\'([^\']+)\'/", $res["body"], $reqs2);
299			preg_match_all("/src=([A-Za-z0-9:\?\=\/\.\-\_]+)\}/", $res["body"], $reqs3);
300			$merge = array_merge($merge, $reqs1[1], $reqs2[1], $reqs3[1]);
301			$merge = array_unique($merge);
302		}
303
304		// Find images in tiki_blog_posts
305		$query = "select `data` from `tiki_blog_posts`";
306		$result = $this->query($query, []);
307
308		while ($res = $result->fetchRow()) {
309			preg_match_all("/src=\"([^\"]+)\"/", $res["data"], $reqs1);
310
311			preg_match_all("/src=\'([^\']+)\'/", $res["data"], $reqs2);
312			preg_match_all("/src=([A-Za-z0-9:\?\=\/\.\-\_]+)\}/", $res["data"], $reqs3);
313			$merge = array_merge($merge, $reqs1[1], $reqs2[1], $reqs3[1]);
314			$merge = array_unique($merge);
315		}
316
317		$positives = [];
318
319		foreach ($merge as $img) {
320			if (strstr($img, 'show_image')) {
321				preg_match("/id=([0-9]+)/", $img, $rq);
322
323				$positives[] = $rq[1];
324			}
325		}
326
327		$query = "select `imageId` from `tiki_images` where `galleryId`=0";
328		$result = $this->query($query, []);
329
330		while ($res = $result->fetchRow()) {
331			$id = $res["imageId"];
332
333			if (! in_array($id, $positives)) {
334				TikiLib::lib('imagegal')->remove_image($id,$GLOBALS['user']);
335			}
336		}
337	}
338
339	/**
340	 * Finds if a name given to a database dump is already in use
341	 *
342	 * @param string $tag
343	 * @return bool     false on no tag existing, true on tag already present
344	 */
345	function tag_exists($tag)
346	{
347		$query = "select distinct `tagName` from `tiki_tags` where `tagName` = ?";
348
349		$result = $this->query($query, [$tag]);
350		return (bool)$result->numRows();
351	}
352
353	/**
354	 *
355	 * Removes a database dump
356	 *
357	 * @param string $tagname
358	 * @return bool     Right now only returns true
359	 */
360	function remove_tag($tagname)
361	{
362		$query = "delete from `tiki_tags` where `tagName`=?";
363		$this->query($query, [$tagname]);
364		TikiLib::lib('logs')->add_log('dump', "removed tag: $tagname");
365		return true;
366		//fixme: This should return false on failure
367	}
368
369	/**
370	 * @return array
371	 */
372	function get_tags()
373	{
374		$query = "select distinct `tagName` from `tiki_tags`";
375
376		$result = $this->query($query, []);
377		$ret = [];
378
379		while ($res = $result->fetchRow()) {
380			$ret[] = $res["tagName"];
381		}
382
383		return $ret;
384	}
385
386	/**
387	 *
388	 * This function can be used to store the set of actual pages in the "tags"
389	 * table preserving the state of the wiki under a tag name.
390	 * @param $tagname
391	 * @see dump()
392	 */
393	function create_tag($tagname)
394	{
395		$query = "select * from `tiki_pages`";
396		$result = $this->query($query, []);
397
398		while ($res = $result->fetchRow()) {
399			$data = $res["data"];
400			$pageName = $res["pageName"];
401			$description = $res["description"];
402			$query = "delete from `tiki_tags`where `tagName`=? and `pageName`=?";
403			$this->query($query, [$tagname, $pageName], -1, -1, false);
404			$query = "insert into `tiki_tags`(`tagName`,`pageName`,`hits`,`data`,`lastModif`,`comment`,`version`,`user`,`ip`,`flag`,`description`)" .
405				" values(?,?,?,?,?,?,?,?,?,?,?)";
406			$this->query(
407				$query,
408				[
409					$tagname,
410					$pageName,
411					$res["hits"],
412					$data,
413					$res["lastModif"],
414					$res["comment"],
415					$res["version"],
416					$res["user"],
417					$res["ip"],
418					$res["flag"],
419					$description
420				]
421			);
422		}
423
424		$logslib = TikiLib::lib('logs');
425		$logslib->add_log('dump', "wiki database dump created: $tagname");
426	}
427
428	/**
429	 * This funcion recovers the state of the wiki using a tagName from the tags table
430	 *
431	 * @param string $tagname
432	 * @return bool     currenty only returns true
433	 */
434	function restore_tag($tagname)
435	{
436
437		$query = "update `tiki_pages` set `cache_timestamp`=0";
438		$this->query($query, []);
439		$query = "select * from `tiki_tags` where `tagName`=?";
440		$result = $this->query($query, [$tagname]);
441
442		while ($res = $result->fetchRow()) {
443			$query = "update `tiki_pages`" .
444				" set `hits`=?,`data`=?,`lastModif`=?,`comment`=?,`version`=`version`+1,`user`=?,`ip`=?,`flag`=?,`description`=?" .
445				"  where `pageName`=?";
446
447			$this->query(
448				$query,
449				[
450					$res["hits"],
451					$res["data"],
452					$res["lastModif"],
453					$res["comment"],
454					$res["user"],
455					$res["ip"],
456					$res["flag"],
457					$res["description"],
458					$res["pageName"]
459				]
460			);
461		}
462
463		TikiLib::lib('logs')->add_log('dump', "recovered tag: $tagname");
464		return true;
465		// fixme: should return false on failure
466	}
467
468	/** Dumps wiki pages to a tar file
469	 * @see create_tag()
470	 */
471	function dump()
472	{
473		global $tikidomain, $prefs;
474		$parserlib = TikiLib::lib('parser');
475
476		$dumpPath = "storage";
477		if ($tikidomain) {
478			$dumpPath .= "/$tikidomain";
479		}
480
481		$dumpPath = $dumpPath . '/dump_wiki.tar';
482		@unlink($dumpPath);
483		$tar = new tar();
484
485		// @fixme: Completely outdated. styles/ no longer exists.
486		//$tar->addFile('styles/' . $prefs['theme']);
487
488		// Foreach page
489		$query = "select * from `tiki_pages`";
490		$result = $this->query($query, []);
491
492		while ($res = $result->fetchRow()) {
493			$pageName = $res["pageName"] . '.html';
494
495			$pageContents = $parserlib->parse_data($res["data"]);
496
497			// Now change tiki-index.php?page=foo to foo.html
498			// and tiki-index.php to HomePage.html
499			$pageContents = preg_replace("/tiki-index.php\?page=([^\'\"\$]+)/", "$1.html", $pageContents);
500			$pageContents = preg_replace("/tiki-editpage.php\?page=([^\'\"\$]+)/", "", $pageContents);
501			//preg_match_all("/tiki-index.php\?page=([^ ]+)/",$dat,$cosas);
502			//print_r($cosas);
503
504			$data = "<html>";
505			$data .= "<head>";
506			$data .= "<title>" . $res["pageName"] . "</title>";
507			// $data .= "<link rel='StyleSheet' href='styles/" . $prefs['style'] . "' type='text/css'>";
508			$data .= '</head>';
509			$data .= "<body><a class='wiki' href='" .
510				$prefs['wikiHomePage'] .
511				".html'>home</a><br /><h1>" .
512				$res["pageName"] .
513				"</h1><div class='wikitext'>" .
514				$pageContents .
515				'</div></body>';
516			$data .= '</html>';
517			$tar->addData($pageName, $data, $res["lastModif"]);
518		}
519
520		$tar->toTar($dumpPath, false);
521		unset($tar);
522		$logslib = TikiLib::lib('logs');
523		$logslib->add_log('dump', 'wiki file dump created in ' . $dumpPath);
524	}
525
526	/**
527	 * Validates if the php version is fully compatible with OPCache.
528	 * @return bool
529	 */
530	function checkOPCacheCompatibility()
531	{
532		return ! ((version_compare(PHP_VERSION, '7.1.0', '>=') && version_compare(PHP_VERSION, '7.2.0', '<')) //7.1.x
533			|| (version_compare(PHP_VERSION, '7.2.0', '>=') && version_compare(PHP_VERSION, '7.2.19', '<')) // >= 7.2.0 < 7.2.19
534			|| (version_compare(PHP_VERSION, '7.3.0', '>=') && version_compare(PHP_VERSION, '7.3.6', '<'))); // >= 7.3.0 < 7.3.6
535	}
536
537	public function getOpcodeCacheStatus()
538	{
539		$opcode_stats = [
540			'opcode_cache' => null,
541			'stat_flag' => null,
542			'warning_check' => false,
543			'warning_fresh' => false,
544			'warning_ratio' => false,
545			'warning_starve' => false,
546			'warning_low' => false,
547			'warning_xcache_blocked' => false,
548		];
549
550		if (function_exists('apc_sma_info') && ini_get('apc.enabled')) {
551			if ($_REQUEST['apc_clear'] && TikiLib::lib('access')->checkCsrf()) {
552				$results['system'] = apc_clear_cache();
553				$results['user'] = apc_clear_cache('user');
554				$results['opcode'] = apc_clear_cache('opcode');
555				if (! in_array(false, $results, true)) {
556					Feedback::success(tr('APC system, user and opcode caches cleared'));
557				} else {
558					foreach ($results as $resultType => $result) {
559						if ($result === true) {
560							Feedback::success(tr('APC %0 cache cleared', $resultType));
561						} elseif ($result === false) {
562							Feedback::error(tr('APC %0 cache not cleared', $resultType));
563						}
564					}
565				}
566			}
567
568			$sma = apc_sma_info();
569			$mem_total = $sma['num_seg'] * $sma['seg_size'];
570
571			$cache = apc_cache_info(null, true);
572			$hit_total = $cache['num_hits'] + $cache['num_misses'];
573			if (! $hit_total) {    // cheat for chart after cache clear
574				$hit_total = 1;
575				$cache['num_misses'] = 1;
576			}
577
578			$opcode_stats = [
579				'opcode_cache' => 'APC',
580				'stat_flag' => 'apc.stat',
581				'memory_used' => ($mem_total - $sma['avail_mem']) / $mem_total,
582				'memory_avail' => $sma['avail_mem'] / $mem_total,
583				'memory_total' => $mem_total,
584				'hit_hit' => $cache['num_hits'] / $hit_total,
585				'hit_miss' => $cache['num_misses'] / $hit_total,
586				'hit_total' => $hit_total,
587				'type' => 'apc',
588			];
589		} elseif (function_exists('xcache_info') && (ini_get('xcache.cacher') == '1' || ini_get('xcache.cacher') == 'On')) {
590			if (ini_get('xcache.admin.enable_auth') == '1' || ini_get('xcache.admin.enable_auth') == 'On') {
591				$opcode_stats['warning_xcache_blocked'] = true;
592			} else {
593				$opcode_stats = [
594					'stat_flag' => 'xcache.stat',
595					'memory_used' => 0,
596					'memory_avail' => 0,
597					'memory_total' => 0,
598					'hit_hit' => 0,
599					'hit_miss' => 0,
600					'hit_total' => 0,
601					'type' => 'xcache',
602				];
603
604				foreach (range(0, xcache_count(XC_TYPE_PHP) - 1) as $index) {
605					$info = xcache_info(XC_TYPE_PHP, $index);
606
607					$opcode_stats['hit_hit'] += $info['hits'];
608					$opcode_stats['hit_miss'] += $info['misses'];
609					$opcode_stats['hit_total'] += $info['hits'] + $info['misses'];
610
611					$opcode_stats['memory_used'] += $info['size'] - $info['avail'];
612					$opcode_stats['memory_avail'] += $info['avail'];
613					$opcode_stats['memory_total'] += $info['size'];
614				}
615
616				$opcode_stats['memory_used'] /= $opcode_stats['memory_total'];
617				$opcode_stats['memory_avail'] /= $opcode_stats['memory_total'];
618				$opcode_stats['hit_hit'] /= $opcode_stats['hit_total'];
619				$opcode_stats['hit_miss'] /= $opcode_stats['hit_total'];
620			}
621			$opcode_stats['opcode_cache'] = 'XCache';
622		} elseif (function_exists('wincache_fcache_fileinfo')) {
623			// Wincache is installed
624
625			// Determine if version 1 or 2 is used. Version 2 does not support ocache
626
627			if (function_exists('wincache_ocache_fileinfo')) {
628				// Wincache version 1
629				if (ini_get('wincache.ocenabled') == '1') {
630					$opcode_stats = [
631						'opcode_cache' => 'WinCache',
632						'stat_flag' => 'wincache.ocenabled',
633						'memory_used' => 0,
634						'memory_avail' => 0,
635						'memory_total' => 0,
636						'hit_hit' => 0,
637						'hit_miss' => 0,
638						'hit_total' => 0,
639						'type' => 'wincache',
640					];
641
642					$info = wincache_ocache_fileinfo();
643				}
644			} else {
645				// Wincache version 2 or higher
646				if (ini_get('wincache.fcenabled') == '1') {
647					$opcode_stats = [
648						'opcode_cache' => 'WinCache',
649						'stat_flag' => 'wincache.fcenabled',
650						'memory_used' => 0,
651						'memory_avail' => 0,
652						'memory_total' => 0,
653						'hit_hit' => 0,
654						'hit_miss' => 0,
655						'hit_total' => 0,
656						'type' => 'wincache',
657					];
658					$info = wincache_fcache_fileinfo();
659				}
660			}
661			if (! empty($opcode_stats['stat_flag'])) {
662				$opcode_stats['hit_hit'] = $info['total_hit_count'];
663				$opcode_stats['hit_miss'] = $info['total_miss_count'];
664				$opcode_stats['hit_total'] = $info['total_hit_count'] + $info['total_miss_count'];
665
666				$memory = wincache_fcache_meminfo();
667				$opcode_stats['memory_avail'] = $memory['memory_free'];
668				$opcode_stats['memory_total'] = $memory['memory_total'];
669				$opcode_stats['memory_used'] = $memory['memory_total'] - $memory['memory_free'];
670
671				$opcode_stats['memory_used'] /= $opcode_stats['memory_total'];
672				$opcode_stats['memory_avail'] /= $opcode_stats['memory_total'];
673				$opcode_stats['hit_hit'] /= $opcode_stats['hit_total'];
674				$opcode_stats['hit_miss'] /= $opcode_stats['hit_total'];
675			}
676		} elseif (function_exists('opcache_get_status') && ini_get('opcache.enable') == '1') {
677			$opcode_stats['opcode_cache'] = 'OpCache';
678			$status = opcache_get_status();
679
680			$opcode_stats['hit_hit'] = $status['opcache_statistics']['hits'];
681			$opcode_stats['hit_miss'] = $status['opcache_statistics']['misses'];
682			$opcode_stats['hit_total'] = $status['opcache_statistics']['hits'] + $status['opcache_statistics']['misses'];
683
684			$opcode_stats['memory_avail'] = $status['memory_usage']['free_memory'];
685			$opcode_stats['memory_used'] = $status['memory_usage']['used_memory'];
686			$opcode_stats['memory_total'] = $status['memory_usage']['used_memory'] + $status['memory_usage']['free_memory'];
687
688			$opcode_stats['memory_used'] /= $opcode_stats['memory_total'];
689			$opcode_stats['memory_avail'] /= $opcode_stats['memory_total'];
690			$opcode_stats['hit_hit'] /= $opcode_stats['hit_total'];
691			$opcode_stats['hit_miss'] /= $opcode_stats['hit_total'];
692		}
693
694		// Make results easier to read
695		$opcode_stats['memory_used'] = round($opcode_stats['memory_used'], 2);
696		$opcode_stats['memory_avail'] = round($opcode_stats['memory_avail'], 2);
697		$opcode_stats['hit_hit'] = round($opcode_stats['hit_hit'], 2);
698		$opcode_stats['hit_miss'] = round($opcode_stats['hit_miss'], 2);
699
700		if (isset($opcode_stats['hit_total'])) {
701			$opcode_stats = array_merge(
702				$opcode_stats,
703				[
704					'warning_fresh' => $opcode_stats['hit_total'] < 10000,
705					'warning_ratio' => $opcode_stats['hit_hit'] < 0.8,
706				]
707			);
708		}
709
710		if (isset($opcode_stats['memory_total'])) {
711			$opcode_stats = array_merge(
712				$opcode_stats,
713				[
714					'warning_starve' => $opcode_stats['memory_avail'] < 0.2,
715					'warning_low' => $opcode_stats['memory_total'] < 60 * 1024 * 1024,
716				]
717			);
718		}
719
720		$stat_flag = $opcode_stats['stat_flag'];
721		if ($stat_flag) {
722			$opcode_stats['warning_check'] = (bool)ini_get($stat_flag);
723		}
724
725		return $opcode_stats;
726	}
727
728	/**
729	 * Check if System Configuration file has "ini" extension and is under the tiki installation (likely web accessible)
730	 *
731	 * @return bool
732	 */
733	public function checkSystemConfigurationFile()
734	{
735		$show_warning = false;
736
737		$db_file = 'db/local.php';
738		if (file_exists($db_file)) {
739			include($db_file);
740
741			if (isset($system_configuration_file) && file_exists($system_configuration_file)) {
742				$tikiPath = realpath(TIKI_PATH);
743				$configPath = realpath($system_configuration_file);
744				if (strncmp($tikiPath, $configPath, strlen($tikiPath)) == 0) {
745					$file_extension = pathinfo($system_configuration_file, PATHINFO_EXTENSION);
746					if ($file_extension == 'ini') {
747						$show_warning = true;
748					}
749				}
750			}
751		}
752
753		return $show_warning;
754	}
755}
756