protected function validate_params($linkname, $urltemplate, $mappings) {
        // Check linkname is A-Z0-9
        if (empty($linkname) || preg_match('/[^A-Z0-9]/', $linkname)) {
            throw new restore_decode_rule_exception('decode_rule_incorrect_name', $linkname);
        }
        // Look urltemplate starts by /
        if (empty($urltemplate) || substr($urltemplate, 0, 1) != '/') {
            throw new restore_decode_rule_exception('decode_rule_incorrect_urltemplate', $urltemplate);
        }
        if (!is_array($mappings)) {
            $mappings = array($mappings);
        }
        // Look for placeholders in template
        $countph = preg_match_all('/(\$\d+)/', $urltemplate, $matches);
        $countma = count($mappings);
        // Check mappings number matches placeholders
        if ($countph != $countma) {
            $a = new stdClass();
            $a->placeholders = $countph;
            $a->mappings     = $countma;
            throw new restore_decode_rule_exception('decode_rule_mappings_incorrect_count', $a);
        }
        // Verify they are consecutive (starting on 1)
        $smatches = str_replace('$', '', $matches[1]);
        sort($smatches, SORT_NUMERIC);
        if (reset($smatches) != 1 || end($smatches) != $countma) {
            throw new restore_decode_rule_exception('decode_rule_nonconsecutive_placeholders', implode(', ', $smatches));
        }
        // No dupes in placeholders
        if (count($smatches) != count(array_unique($smatches))) {
            throw new restore_decode_rule_exception('decode_rule_duplicate_placeholders', implode(', ', $smatches));
        }

        // Return one array of placeholders as keys and mappings as values
        return array_combine($smatches, $mappings);
    }