protected function applyArguments($argDef, $argValues)
{
$storeEnv = $this->getStoreEnv();
$env = new Environment();
$env->store = $storeEnv->store;
$hasVariable = false;
$args = [];
foreach ($argDef as $i => $arg) {
list($name, $default, $isVariable) = $argDef[$i];
$args[$name] = [$i, $name, $default, $isVariable];
$hasVariable |= $isVariable;
}
$keywordArgs = [];
$deferredKeywordArgs = [];
$remaining = [];
// assign the keyword args
foreach ((array) $argValues as $arg) {
if (!empty($arg[0])) {
if (!isset($args[$arg[0][1]])) {
if ($hasVariable) {
$deferredKeywordArgs[$arg[0][1]] = $arg[1];
} else {
$this->throwError("Mixin or function doesn't have an argument named \$%s.", $arg[0][1]);
break;
}
} elseif ($args[$arg[0][1]][0] < count($remaining)) {
$this->throwError("The argument \$%s was passed both by position and by name.", $arg[0][1]);
break;
} else {
$keywordArgs[$arg[0][1]] = $arg[1];
}
} elseif (count($keywordArgs)) {
$this->throwError('Positional arguments must come before keyword arguments.');
break;
} elseif ($arg[2] === true) {
$val = $this->reduce($arg[1], true);
if ($val[0] === Type::T_LIST) {
foreach ($val[2] as $name => $item) {
if (!is_numeric($name)) {
$keywordArgs[$name] = $item;
} else {
$remaining[] = $item;
}
}
} elseif ($val[0] === Type::T_MAP) {
foreach ($val[1] as $i => $name) {
$name = $this->compileStringContent($this->coerceString($name));
$item = $val[2][$i];
if (!is_numeric($name)) {
$keywordArgs[$name] = $item;
} else {
$remaining[] = $item;
}
}
} else {
$remaining[] = $val;
}
} else {
$remaining[] = $arg[1];
}
}
foreach ($args as $arg) {
list($i, $name, $default, $isVariable) = $arg;
if ($isVariable) {
$val = [Type::T_LIST, ',', [], $isVariable];
for ($count = count($remaining); $i < $count; $i++) {
$val[2][] = $remaining[$i];
}
foreach ($deferredKeywordArgs as $itemName => $item) {
$val[2][$itemName] = $item;
}
} elseif (isset($remaining[$i])) {
$val = $remaining[$i];
} elseif (isset($keywordArgs[$name])) {
$val = $keywordArgs[$name];
} elseif (!empty($default)) {
continue;
} else {
$this->throwError("Missing argument {$name}");
break;
}
$this->set($name, $this->reduce($val, true), true, $env);
}
$storeEnv->store = $env->store;
foreach ($args as $arg) {
list($i, $name, $default, $isVariable) = $arg;
if ($isVariable || isset($remaining[$i]) || isset($keywordArgs[$name]) || empty($default)) {
continue;
}
$this->set($name, $this->reduce($default, true), true);
}
}