Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
S
Shipping by Rules Library
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Releases
Container registry
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Open Tools
Shipping by Rules Library
Commits
3066c2ed
Commit
3066c2ed
authored
8 years ago
by
Reinhold Kainhofer
Browse files
Options
Downloads
Patches
Plain Diff
Merge in changes from VM and WC
parent
f9050f24
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
rules_shipping_framework.php
+92
-48
92 additions, 48 deletions
rules_shipping_framework.php
with
92 additions
and
48 deletions
rules_shipping_framework.php
+
92
−
48
View file @
3066c2ed
...
...
@@ -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
*/
p
rotected
function
getAddressZIP
(
$zip
)
{
p
ublic
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
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment