diff --git a/rules_shipping_framework.php b/rules_shipping_framework.php
index 7ec0ecf8b3fb2875417e4a21eab193fee0527d33..0c5123cb64119fb01fd43121a8ecb71013cf63bb 100644
--- a/rules_shipping_framework.php
+++ b/rules_shipping_framework.php
@@ -51,11 +51,12 @@ function is_equal($a, $b) {
 
 class RulesShippingFramework {
 	static $_version = "0.1";
-	protected $_callbacks = array();
+	protected $callbacks = array();
 	// Store the parsed and possibly evaluated rules for each method (method ID is used as key)
 	protected $rules = array();
 	protected $match = array();
-	var $custom_functions = array ();
+	protected $custom_functions = array ();
+	protected $available_scopings = array();
 	
 	function __construct() {
 // 		$this->registerCallback('addCustomCartValues',	array($this, 'addCustomCartValues'));
@@ -79,6 +80,24 @@ class RulesShippingFramework {
 		$this->callbacks[$callback] = $func;
 	}
 	
+	/**
+	 * Register all possible scopings to the framework in the form
+	 *    array("skus" => "products" , "products" => "products")
+	 * This registers functions evaluate_for_skus and evaluate_for_products,
+	 * which both filter products (they are identical).
+	 */
+	 public function registerScopings($scopings) {
+		$this->available_scopings = $scopings;
+	}
+	
+	/**
+	 * Get the list of all scopings the framework implementation claims to have
+	 * implemented.
+	 */
+	public function getScopings() {
+		return $this->available_scopings;
+	}
+	
 	public function readableString($string) {
 		switch ($string) {
 			case "OTSHIPMENT_RULES_CUSTOMFUNCTIONS_ALREADY_DEFINED":
@@ -113,6 +132,8 @@ class RulesShippingFramework {
 					return "Unknown operator '%s' in shipment rule '%s'";
 			case "OTSHIPMENT_RULES_UNKNOWN_TYPE":
 					return "Unknown rule type '%s' encountered for rule '%s'";
+			case "OTSHIPMENT_RULES_SCOPING_UNKNOWN":
+					return "Unknown scoping function 'evaluate_for_%s' encountered in rule '%s'";
 			case "OTSHIPMENT_RULES_UNKNOWN_VARIABLE":
 					return "Unknown variable '%s' in rule '%s'";
 			default:
@@ -146,6 +167,10 @@ class RulesShippingFramework {
 		return array ();
 	}
 	
+	function getCustomFunctionDefinitions() {
+		return $this->custom_functions;
+	}
+	
 	/** @tag system-specific
 	 *  @function printWarning()
 	 *    Print a warning in the system-specific way.
@@ -182,22 +207,14 @@ class RulesShippingFramework {
 	 */
 	public function setup() {
 		$custfuncdefs = $this->getCustomFunctions();
-		// Loop through the return values of all plugins:
-		foreach ($custfuncdefs as $custfuncs) {
-			if (empty($custfuncs))
-				continue;
-			if (!is_array($custfuncs)) {
-				$this->warning('OTSHIPMENT_RULES_CUSTOMFUNCTIONS_NOARRAY');
-			}
-			// Now loop through all custom function definitions of this plugin
-			// If a function was registered before, print a warning and use the first definition
-			foreach ($custfuncs as $fname => $func) {
-				if (isset($this->custom_functions[$fname])) {
-					$this->warning('OTSHIPMENT_RULES_CUSTOMFUNCTIONS_ALREADY_DEFINED', $fname);
-				} else {
-					$this->debug("Defining custom function $fname");
-					$this->custom_functions[strtolower($fname)] = $func;
-				}
+		// Now loop through all custom function definitions of this plugin
+		// If a function was registered before, print a warning and use the first definition
+		foreach ($custfuncdefs as $fname => $func) {
+			if (isset($this->custom_functions[$fname]) && $this->custom_functions[$fname]!=$custfuncs[$fname]) {
+				$this->warning('OTSHIPMENT_RULES_CUSTOMFUNCTIONS_ALREADY_DEFINED', $fname);
+			} else {
+				$this->debug("Defining custom function $fname");
+				$this->custom_functions[strtolower($fname)] = $func;
 			}
 		}
 	}
@@ -266,10 +283,18 @@ class RulesShippingFramework {
 		return array();
 	}
 	
+	protected function getDebugVariables ($cart, $products, $method) {
+		
+		return array(
+			'debug_cart'=> print_r($cart,1),
+			'debug_products' => print_r($products, 1),
+		);
+	}
+	
 	/** 
 	 * Extract information about non-numerical zip codes (UK and Canada) from the postal code
 	 */
-	protected function getAddressZIP ($zip) {
+	public function getAddressZIP ($zip) {
 		$values = array();
 
 		// Postal code Check for UK postal codes: Use regexp to determine if ZIP structure matches and also to extract the parts.
@@ -309,11 +334,14 @@ class RulesShippingFramework {
 	/** Allow child classes to add additional variables for the rules or modify existing one
 	 */
 	protected function addCustomCartValues ($cart, $products, $method, &$values) {
+		// Pass all args through to the callback, if it exists
 		if (isset($this->callbacks['addCustomCartValues'])) {
-			return $this->callbacks['addCustomCartValues']($cart, $products, $method, $values);
+			return call_user_func_array($this->callbacks['addCustomCartValues'], array($cart, $products, $method, &$values)/*func_get_args()*/);
 		}
+		return $values;
 	}
 	protected function addPluginCartValues($cart, $products, $method, &$values) {
+		return $values;
 	}
 	
 	public function getCartValues ($cart, $products, $method) {
@@ -328,7 +356,8 @@ class RulesShippingFramework {
 			$this->getOrderAddress ($cart, $method),
 			// Add Total/Min/Max weight and dimension variables:
 			$this->getOrderWeights ($cart, $products, $method),
-			$this->getOrderDimensions ($cart, $products, $method)
+			$this->getOrderDimensions ($cart, $products, $method),
+			$this->getDebugVariables ($cart, $products, $method)
 		);
 		// Let child classes update the $cartvals array, or add new variables
 		$this->addCustomCartValues($cart, $products, $method, $cartvals);
@@ -396,7 +425,7 @@ class RulesShippingFramework {
 			}
 		}
 		// None of the rules matched, so return NULL, but keep the evaluated results;
-		$this->match[$id] = $result;
+		$this->match[$id] = NULL;
 		return NULL;
 	}
 
@@ -424,7 +453,7 @@ class RulesShippingFramework {
 			$this->parseMethodRules($method);
 		// TODO: This needs to be redone sooner or later!
 		$match = $this->evaluateMethodRules ($cart, $method);
-		if ($match && !is_null ($match['rule'])) {
+		if ($match && isset($match['rule']) && !is_null ($match['rule'])) {
 			$this->setMethodCosts($method, $match, null);
 			// If NoShipping is set, this method should NOT offer any shipping at all, so return FALSE, otherwise TRUE
 			// If the rule has a name, print it as warning (otherwise don't print anything)
@@ -455,7 +484,7 @@ class RulesShippingFramework {
 		if (!isset($this->rules[$id])) 
 			$this->parseMethodRules($method);
 		$match = $this->evaluateMethodRules ($cart, $method);
-		if ($match) {
+		if ($match && isset($match['rule']) && !is_null ($match['rule'])) {
 			if ($this->handleNoShipping($match, $method)) {
 				return $results;
 			}
@@ -509,7 +538,8 @@ class RulesShippingFramework {
 
 	protected function createMethodRule ($r, $countries, $ruleinfo) {
 		if (isset($this->callbacks['initRule'])) {
-			return $this->callbacks['initRule']($this, $r, $countries, $ruleinfo);
+			return call_user_func_array($this->callbacks['initRule'], 
+										array($this, $r, $countries, $ruleinfo));
 		} else {
 			return new ShippingRule($this, $r, $countries, $ruleinfo);
 		}
@@ -528,7 +558,7 @@ class RulesShippingFramework {
 		$rules1 = preg_split("/(\r\n|\n|\r)/", $rulestring);
 		foreach ($rules1 as $r) {
 			// Ignore empty lines
-			if (empty($r)) continue;
+			if (empty($r) || trim($r)=='') continue;
 			$result[] = $this->createMethodRule ($r, $countries, $ruleinfo);
 		}
 		return $result;
@@ -614,7 +644,7 @@ class ShippingRule {
 		/* In the advanced version, all conditions and costs can be given as a full mathematical expression */
 		/* Both versions create an expression tree, which can be easily evaluated in evaluateTerm */
 		$rulepart = trim($rulepart);
-		if (empty($rulepart)) return;
+		if (!isset($rulepart) || $rulepart==='') return;
 
 		
 		// Special-case the name assignment, where we don't want to interpret the value as an arithmetic expression!
@@ -764,24 +794,31 @@ class ShippingRule {
 		}
 	}
 	
+	protected function normalizeScoping($scoping) {
+		$scopings = $this->framework->getScopings();
+// $this->framework->warning("<pre>normalizing Scoping $scoping. Registered scopings are: ".print_r($scopings,1)."</pre>");
+		if (isset($scopings[$scoping])) {
+			return $scopings[$scoping];
+		} else {
+			return false;
+		}
+	}
+	
 	/** Evaluate the given expression $expr only for the products that match the filter given by the scoping 
 	 * function and the corresponding conditions */
 	protected function evaluateScoping($expr, $scoping, $conditionvals, $vals, $products, $cartvals_callback) {
 		if (count($conditionvals)<1)
 			return $this->evaluateTerm($expr, $vals, $products, $cartvals_callback);
-
-		// TODO: Make this more general!
-		$filterkeys = array( 
-			"evaluate_for_categories" =>    'categories',
-			"evaluate_for_products" =>      'products',
-			"evaluate_for_skus" =>      'products',
-			"evaluate_for_vendors" =>       'vendors',
-			"evaluate_for_manufacturers" => 'manufacturers',
-		);
 		
-		$conditions = array();
-		if (isset($filterkeys[$scoping])) 
-			$conditions[$filterkeys[$scoping]] = $conditionvals;
+// $this->framework->warning("<pre>evaluating scoping $scoping of expression ".print_r($expr,1)." with conditions ".print_r($conditionvals,1)."</pre>");
+		// Normalize aliases (e.g. 'skus' and 'products' usually indicate the same scoping
+		$normalizedScoping = $this->normalizeScoping($scoping);
+		if (!$normalizedScoping) {
+			$this->framework->warning('OTSHIPMENT_RULES_SCOPING_UNKNOWN', $scoping, $this->rulestring);
+			return false;
+		} else {
+			$conditions = array($normalizedScoping => $conditionvals);
+		}
 
 		// Pass the conditions to the parent plugin class to filter the current list of products:
 		$filteredproducts = $this->framework->filterProducts($products, $conditions);
@@ -794,9 +831,10 @@ class ShippingRule {
 		$func = strtolower($function);
 		// Check if we have a custom function definition and use that if so.
 		// This is done first to allow plugins to override even built-in functions!
-		if (isset($this->plugin->custom_functions[$func])) {
+		$customfunctions = $this->framework->getCustomFunctionDefinitions();
+		if (isset($customfunctions[$func])) {
 			$this->framework->debug("Evaluating custom function $function, defined by a plugin");
-			return call_user_func_array($this->plugin->custom_functions[$func], $args, $this);
+			return call_user_func($customfunctions[$func], $args, $this);
 		}
 
 		// Functions with no argument:
@@ -878,8 +916,11 @@ class ShippingRule {
 			return $varname;
 		} elseif ($varname=='values') {
 			return $vals;
-		} elseif ($varname=='values_debug') {
-			return print_r($vals,1);
+		} elseif ($varname=='values_debug' || $varname=='debug_values') {
+			$tmpvals = $vals;
+			unset($tmpvals['debug_cart']);
+			unset($tmpvals['debug_products']);
+			return print_r($tmpvals,1);
 		} else {
 			$this->framework->warning('OTSHIPMENT_RULES_EVALUATE_UNKNOWN_VALUE', $expr, $this->rulestring);
 			return null;
@@ -890,8 +931,7 @@ class ShippingRule {
 		// The scoping functions need to be handled differently, because they first need to adjust the cart variables to the filtered product list
 		// before evaluating its first argument. So even though parsing the rules handles scoping functions like any other function, their 
 		// evaluation is fundamentally different and is special-cased here:
-		$scoping_functions = array("evaluate_for_categories", "evaluate_for_products", "evaluate_for_vendors", "evaluate_for_manufacturers");
-		$is_scoping = is_array($expr) && ($expr[0]=="FUNCTION") && (count($expr)>1) && in_array($expr[1], $scoping_functions);
+		$is_scoping = is_array($expr) && ($expr[0]=="FUNCTION") && (count($expr)>1) && (substr($expr[1], 0, 13)==="evaluate_for_");
 
 		if (is_null($expr)) {
 			return $expr;
@@ -906,10 +946,14 @@ class ShippingRule {
 			}
 		} elseif ($is_scoping) {
 			$op = array_shift($expr); // ignore the "FUNCTION"
-			$func = array_shift($expr); // The scoping function name
+			$scope = substr(array_shift($expr), 13); // The scoping function name with "evaluate_for_" cut off
 			$expression = array_shift($expr); // The expression to be evaluated
-			$conditions = $expr; // the remaining $expr list now contains the conditions
-			return $this->evaluateScoping ($expression, $func, $conditions, $vals, $products, $cartvals_callback);
+			// the remaining $expr list now contains the conditions. Evaluate them one by one:
+			$conditions = array();
+			foreach ($expr as $e) {
+				$conditions[] = $this->evaluateTerm($e, $vals, $products, $cartvals_callback);
+			}
+			return $this->evaluateScoping ($expression, $scope, $conditions, $vals, $products, $cartvals_callback);
 			
 		} elseif (is_array($expr)) {
 			// Operator