Commits

Anonymous committed fb4db5e

Plugin file caching. All plugins will be cached to the file system in the private plugin folder.

Comments (0)

Files changed (3)

textpattern/include/txp_plugin.php

 		extract(doSlash(gpsa(array('name', 'code'))));
 
 		safe_update('txp_plugin', "code = '$code'", "name = '$name'");
+		
+		expire_cached_plugin($name);
 
 		$message = gTxt('plugin_saved', array('{name}' => $name));
 
 					$help = $textile->TextileRestricted($help_raw, 0, 0);
 			}
 
-			if (!empty($prefs['plugin_dir_name']))
+			$plugin_dir = get_plugin_private_folder($name);
+			
+			if (!empty($plugin_dir))
 			{
-				$plugin_dir = txpath.DS.$prefs['plugin_dir_name'].DS.sanitizeFilename($name);
-
 				if (!file_exists($plugin_dir))
 				{
 					mkdir($plugin_dir, '0666', true);
 
 				if ($exists)
 				{
+					expire_cached_plugin($name);
+					
 					$rs = safe_update(
 					   "txp_plugin",
 						"status      = 0,

textpattern/index.php

 		if (!empty($admin_side_plugins) and gps('event') != 'plugin')
 			load_plugins(1);
 
+
 		include txpath.'/lib/txplib_head.php';
 
 		// ugly hack, for the people that don't update their admin_config.php

textpattern/lib/lib_plugin.php

 <?php
+
+// -------------------------------------------------------------
+	function load_plugin_from_file($filename, $name, $version=false, $error_handler='pluginErrorHandler')
+	{
+		global $plugins, $plugins_ver, $txp_current_plugin;
+
+		if (is_file($filename) && !empty($name) && !empty($error_handler))
+		{
+			$plugins[] = $name;
+			$txp_current_plugin = $name;
+			set_error_handler($error_handler);
+
+			include($filename);
+
+			if ($version === false)
+			{
+				if (isset($GLOBALS['xp_current_plugin_ver']))
+					$version = $GLOBALS['xp_current_plugin_ver'];
+				elseif (isset($plugin) && isset($plugin['version']))
+					$version = $plugin['version'];
+			}
+
+			$plugins_ver[$name] = $version;
+			restore_error_handler();
+			
+			unset($GLOBALS['xp_current_plugin_ver']);
+			unset($GLOBALS['txp_current_plugin']);
+			
+			return true;
+		}
+		
+		return false;
+	}
+
+// -------------------------------------------------------------
+	function eval_plugin($name, $code, $version=false, $error_handler='pluginErrorHandler')
+	{
+		global $plugins, $plugins_ver, $txp_current_plugin;
+		
+		if (!empty($name) && !empty($code) && !empty($error_handler))
+		{
+			$plugins[] = $name;
+			$txp_current_plugin = $name;
+			set_error_handler($error_handler);
+			eval($code);			
+			$plugins_ver[$name] = $version;
+			restore_error_handler();
+			
+			unset($GLOBALS['txp_current_plugin']);
+			
+			return true;
+		}
+		
+		return false;
+	}
+
 // -------------------------------------------------------------
 	function load_plugin($name,$ignore_status=false)
 	{
 			return true;
 		}
 
-		if (!empty($prefs['plugin_cache_dir'])) {
+		$plugin_caches = array();
+
+		if (!empty($prefs['plugin_cache_dir']))
+		{
 			$dir = rtrim($prefs['plugin_cache_dir'], '/') . '/';
 			# in case it's a relative path
 			if (!is_dir($dir))
+			{
 				$dir = rtrim(realpath(txpath.'/'.$dir), '/') . '/';
-			if (is_file($dir . $name . '.php')) {
-				$plugins[] = $name;
-				set_error_handler("pluginErrorHandler");
-				$txp_current_plugin = $name;
-				include($dir . $name . '.php');
-				$plugins_ver[$name] = @$plugin['version'];
-				restore_error_handler();
+			}
+
+			$plugin_caches[] = $dir;
+		}
+
+		$plugin_dir = get_plugin_private_folder($name);
+		
+		if (!empty($plugin_dir))
+		{
+			// look in external files folder for code cache
+			$plugin_caches[] = $plugin_dir . DS . '_cached_';
+		}
+
+		foreach($plugin_caches as $d)
+		{
+			if (load_plugin_from_file($d.$name.'.php', $name))
+			{
 				return true;
 			}
 		}
 		$status = $ignore_status ? '' : 'status = 1 AND ';
 
 		$rs = safe_row("name,code,version","txp_plugin",$status ."name='".doSlash($name)."'");
-		if ($rs) {
-			$plugins[] = $rs['name'];
-			$plugins_ver[$rs['name']] = $rs['version'];
+		if ($rs)
+		{
+			$loaded = false;
 
-			set_error_handler("pluginErrorHandler");
-			$txp_current_plugin = $rs['name'];
-			eval($rs['code']);
-			restore_error_handler();
+			if (!empty($plugin_dir))
+			{
+				@mkdir($plugin_dir, '0666', true);
 
-			return true;
+				$cached_file = $plugin_dir . DS . '_cached_' . sanitizeFilename($name) . '.php';
+
+				if (!file_exists($cached_file))
+				{
+					// cache plugin version
+					$code = "<?php\n"
+								. '$GLOBALS[\'xp_current_plugin_ver\'] = \''.$rs['version']."';\n"
+								. $rs['code'] 
+								. "\n?>";
+					file_put_contents($cached_file, $code);
+				}
+				
+				if (file_exists($cached_file))
+				{
+					$loaded = load_plugin_from_file($cached_file, $name, $rs['version']);
+				}
+			}
+
+			// fallback to eval
+			if (!$loaded)
+				$loaded = eval_plugin($rs['name'], $rs['code'], $rs['version']);
+
+			return $loaded;
 		}
 
 		return false;
 	}
+	
+
+// -------------------------------------------------------------
+	function get_plugin_private_folder($name)
+	{
+		global $prefs;
+		
+		if (!empty($prefs['plugin_dir_name']))
+		{
+			return realpath(txpath.DS.$prefs['plugin_dir_name'] . DS) . sanitizeFilename($name);
+		}
+		
+		return '';
+	}
+
+// -------------------------------------------------------------
+	function expire_cached_plugin($name)
+	{
+		$plugin_dir = get_plugin_private_folder($name);
+		if (!empty($plugin_dir))
+		{
+			$cached_file = $plugin_dir . DS . '_cached_' .sanitizeFilename($name) . '.php';
+		
+			if (is_file($cached_file))
+			{
+				unlink($cached_file);
+			}
+		}
+	}
 
 // -------------------------------------------------------------
 	function require_plugin($name)
 // -------------------------------------------------------------
 	function load_plugins($type=NULL)
 	{
-		global $prefs,$plugins, $plugins_ver;
+		global $prefs, $plugins, $plugins_ver;
 
 		if (!is_array($plugins)) $plugins = array();
 
-		if (!empty($prefs['plugin_cache_dir'])) {
+		if (!empty($prefs['plugin_cache_dir']))
+		{
 			$dir = rtrim($prefs['plugin_cache_dir'], DS) . DS;
 			# allow a relative path
-			if (!is_dir($dir)) {
+			if (!is_dir($dir))
+			{
 				$dir = realpath(txpath.DS.$dir);
 				if($dir) $dir = rtrim($dir, DS) . DS;
 			}
 			$dh = @opendir($dir);
-			while ($dh and false !== ($f = @readdir($dh))) {
+			while ($dh and false !== ($f = @readdir($dh)))
+			{
 				if ($f{0} != '.')
-					load_plugin(basename($f, '.php'));
+					load_plugin_from_file($f, basename($f, '.php'));
 			}
 		}
 
 			$where .= (" and type='".doSlash($type)."'");
 
 		$rs = safe_rows("name, code, version", "txp_plugin", $where. ' order by load_order');
-		if ($rs) {
-			$old_error_handler = set_error_handler("pluginErrorHandler");
-			foreach($rs as $a) {
-				if (!in_array($a['name'],$plugins)) {
-					$plugins[] = $a['name'];
-					$plugins_ver[$a['name']] = $a['version'];
-					$GLOBALS['txp_current_plugin'] = $a['name'];
-					$eval_ok = eval($a['code']);
-					if ($eval_ok === FALSE)
+		if ($rs)
+		{
+			foreach($rs as $a)
+			{
+				if (!in_array($a['name'],$plugins))
+				{
+					$loaded = false;
+
+					$plugin_dir = get_plugin_private_folder($a['name']);
+					if (!empty($plugin_dir))
+					{
+						$cached_file = $plugin_dir . DS . '_cached_' . sanitizeFilename($a['name']) . '.php';
+		
+						@mkdir($plugin_dir, '0666', true);		
+		
+						if (!file_exists($cached_file))
+						{
+							// cache plugin version
+							$code = "<?php\n"
+										. '$GLOBALS[\'xp_current_plugin_ver\'] = \''.$a['version']."';\n"
+										. $a['code'] 
+										. "\n?>";
+							file_put_contents($cached_file, $code);
+						}
+
+						$loaded = load_plugin_from_file($cached_file, $a['name'], $a['version']);
+					}
+					
+					if (!$loaded)
+					{
+						$loaded = eval_plugin($a['name'], $a['code'], $a['version']);
+					}
+
+					if (!$loaded)
 						echo gTxt('plugin_load_error_above').strong($a['name']).n.br;
-					unset($GLOBALS['txp_current_plugin']);
 				}
 			}
-			restore_error_handler();
 		}
 	}