/** * Validate a CAS string field to make sure that: 1. it fits in the DB, and * 2. that it is syntactically valid. * @param array $errors the errors array that validation is assembling. * @param string $value the submitted value validate. * @param string $fieldname the name of the field add any errors to. * @param string $savesession the array key to save the string to in $this->validationcasstrings. * @param bool|string $notblank false means do nothing (default). A string * will validate that the field is not blank, and if it is, display that error. * @param int $maxlength the maximum allowable length. Defaults to 255. * @return array updated $errors array. */ protected function validate_cas_string($errors, $value, $fieldname, $savesession, $notblank = true, $maxlength = 255) { if ($notblank && '' === trim($value)) { $errors[$fieldname][] = stack_string('nonempty'); } else { if (strlen($value) > $maxlength) { $errors[$fieldname][] = stack_string('strlengtherror'); } else { $casstring = new stack_cas_casstring($value); if (!$casstring->get_valid('t')) { $errors[$fieldname][] = $casstring->get_errors(); } } } return $errors; }
/** * Validates the options, when needed. * * @return (bool, string) * @access public */ public function validate_atoptions($opt) { if ($this->processcasoptions) { $cs = new stack_cas_casstring($opt); return array($cs->get_valid('t'), $cs->get_errors()); } return array(true, ''); }
public function do_test() { $this->atmark = 1; $anotes = array(); // Note that in casting to an integer we are lucky here. // Non-integer strings get cast to zero, which is invalid anyway.... $atestops = (int) $this->atoption; if (!is_int($atestops) or $atestops <= 0) { $this->aterror = 'TEST_FAILED'; $this->atfeedback = stack_string('TEST_FAILED', array('errors' => '')); $this->atfeedback .= stack_string('ATNumDecPlaces_OptNotInt', array('opt' => $this->atoption)); $this->atansnote = 'ATNumDecPlaces_STACKERROR_Option.'; $this->atmark = 0; $this->atvalid = false; return null; } $commands = array($this->sanskey, $this->tanskey, (string) $this->atoption); foreach ($commands as $com) { $cs = new stack_cas_casstring($com); if (!$cs->get_valid('t', true, 0)) { $this->aterror = 'TEST_FAILED'; $this->atfeedback = stack_string('TEST_FAILED', array('errors' => '')); $this->atfeedback .= stack_string('AT_InvalidOptions', array('errors' => $cs->get_errors())); $this->atansnote = 'ATNumDecPlaces_STACKERROR_Option.'; $this->atmark = 0; $this->atvalid = false; return null; } } // Check that the first expression is a floating point number, // with the right number of decimal places. $sans = explode('.', $this->sanskey); if (2 === count($sans)) { if ($atestops != strlen($sans[1])) { $this->atfeedback .= stack_string('ATNumDecPlaces_Wrong_DPs'); $anotes[] = 'ATNumDecPlaces_Wrong_DPs (' . strlen($sans[1]) . ' <> ' . $atestops . ')'; $this->atmark = 0; } else { $anotes[] = 'ATNumDecPlaces_Correct'; } } else { // No '.' found. $this->atfeedback .= stack_string('ATNumDecPlaces_NoDP'); $anotes[] = 'ATNumDecPlaces_NoDP'; $this->atmark = 0; } // Check that the two numbers evaluate to the same value. $cascommands = array(); $cascommands[] = "caschat2:ev({$this->atoption},simp)"; $cascommands[] = "caschat0:ev(float(round(10^caschat2*{$this->sanskey})/10^caschat2),simp)"; $cascommands[] = "caschat1:ev(float(round(10^caschat2*{$this->tanskey})/10^caschat2),simp)"; $cascommands[] = "caschat3:ev(second(ATAlgEquiv(caschat0,caschat1)),simp)"; $cts = array(); foreach ($cascommands as $com) { $cs = new stack_cas_casstring($com); $cs->get_valid('t', true, 0); $cts[] = $cs; } $session = new stack_cas_session($cts, null, 0); $session->instantiate(); if ('' != $session->get_errors_key('caschat0')) { $this->aterror = 'TEST_FAILED'; $this->atfeedback = stack_string('TEST_FAILED', array('errors' => $session->get_errors_key('caschat0'))); $anotes[] = 'ATNumDecPlaces_STACKERROR_SAns'; $this->atansnote = implode('. ', $anotes) . '.'; $this->atmark = 0; $this->atvalid = false; return null; } if ('' != $session->get_errors_key('caschat1')) { $this->aterror = 'TEST_FAILED'; $this->atfeedback = stack_string('TEST_FAILED', array('errors' => $session->get_errors_key('caschat1'))); $anotes[] = 'ATNumDecPlaces_STACKERROR_TAns'; $this->atansnote = implode('. ', $anotes) . '.'; $this->atmark = 0; $this->atvalid = false; return null; } if ('' != $session->get_errors_key('caschat2')) { $this->aterror = 'TEST_FAILED'; $this->atfeedback = stack_string('TEST_FAILED', array('errors' => '')); $this->atfeedback .= stack_string('AT_InvalidOptions', array('errors' => $session->get_errors_key('caschat2'))); $anotes[] = 'ATNumDecPlaces_STACKERROR_Options.'; $this->atansnote = implode('. ', $anotes) . '.'; $this->atmark = 0; $this->atvalid = false; return null; } if ($session->get_value_key('caschat3') == 'true') { // Note, we only want the mark to *stay* at 1. $this->atmark *= 1; $anotes[] = 'ATNumDecPlaces_Equiv'; } else { $this->atmark = 0; $anotes[] = 'ATNumDecPlaces_Not_equiv'; } $this->atansnote = implode('. ', $anotes) . '.'; if ($this->atmark) { return true; } return false; }
public function test_strings_4() { $s = 'a:["system(\'rm *\')",3*x]'; $at1 = new stack_cas_casstring($s); $this->assertFalse($at1->get_valid('t')); $this->assertEquals('The expression <span class="stacksyntaxexample">system</span> is forbidden.', $at1->get_errors()); }
/** * This is the basic validation of the student's "answer". * This method is only called if the input is not blank. * * Only a few input methods need to modify this method. * For example, Matrix types have two dimensional contents arrays to loop over. * * @param array $contents the content array of the student's input. * @return array of the validity, errors strings and modified contents. */ protected function validate_contents($contents, $forbiddenkeys) { $errors = $this->extra_validation($contents); $valid = !$errors; // Now validate the input as CAS code. $modifiedcontents = array(); $allowwords = $this->get_parameter('allowWords', ''); foreach ($contents as $val) { $answer = new stack_cas_casstring($val); $answer->get_valid('s', $this->get_parameter('strictSyntax', true), $this->get_parameter('insertStars', 0), $allowwords); // Ensure student hasn't used a variable name used by the teacher. if ($forbiddenkeys) { $answer->check_external_forbidden_words($forbiddenkeys); } $forbiddenwords = $this->get_parameter('forbidWords', ''); if ($forbiddenwords) { $answer->check_external_forbidden_words_literal($forbiddenwords); } $modifiedcontents[] = $answer->get_casstring(); $valid = $valid && $answer->get_valid(); $errors .= $answer->get_errors(); } return array($valid, $errors, $modifiedcontents); }
public static function run_test($test) { // Note: What we would really like to do is the following. // $el = stack_input_factory::make('algebraic', 'sans1', 'x'); // $el->set_parameter('insertStars', 1); // $el->set_parameter('strictSyntax', false); // $el->set_parameter('sameType', false); // $cs = $el->validate_student_response($test->rawstring); // However, we want to pull apart the bits to expose where the various errors occur. $cs = new stack_cas_casstring($test->rawstring); $cs->get_valid('s', false, 1); $cs->set_cas_validation_casstring('sans1', true, true, false, null); $phpvalid = $cs->get_valid(); if ($phpvalid) { // Trim off stack_validate_typeless([..], true, true). $phpcasstring = $cs->get_casstring(); $phpcasstring = substr($phpcasstring, 25); $phpcasstring = substr($phpcasstring, 0, strlen($phpcasstring) - 12); $outputphpcasstring = $phpcasstring; } else { $phpcasstring = ''; $outputphpcasstring = 'N/A...'; } $errors = $cs->get_errors(); $passed = true; if ('php_true' === $test->phpvalid) { $expected = true; } else { $expected = false; } if ($phpvalid != $expected) { $passed = false; $errors .= ' ' . stack_string('phpvalidatemismatch'); } if ($phpvalid && $phpcasstring != $test->phpcasstring) { $passed = false; $errors .= ' ' . stack_maxima_format_casstring($phpcasstring) . ' \\(\\neq \\) ' . stack_maxima_format_casstring($test->phpcasstring); } $casvalid = ''; $caserrors = ''; $casvalue = ''; $casdisplay = ''; if ($cs->get_valid()) { $options = new stack_options(); $options->set_option('simplify', false); $session = new stack_cas_session(array($cs), $options, 0); $session->instantiate(); $session = $session->get_session(); $cs = $session[0]; $caserrors = stack_maxima_translate($cs->get_errors()); $casvalue = stack_maxima_format_casstring($cs->get_value()); if ('cas_true' == $test->casvalid) { $casexpected = true; } else { $casexpected = false; } if ('' == $cs->get_value()) { $casvalid = false; } else { $casvalid = true; } if ($casexpected != $casvalid) { $passed = false; $caserrors .= ' ' . stack_string('casvalidatemismatch'); } $casdisplay = $cs->get_display(); } $answernote = $cs->get_answernote(); if ($answernote != $test->ansnotes) { $passed = false; $errors .= ' ' . stack_string('ansnotemismatch'); } return array($passed, $phpvalid, $phpcasstring, $errors, $casvalid, $caserrors, $casdisplay, $casvalue, $answernote); }
/** * Extract the CAS commands from the string * * @access public * @return bool false if no commands to extract, true if succeeds. */ private function extract_cas_commands() { // First check contains @s. $count = preg_match_all('~(?<!@)@(?!@)~', $this->trimmedcastext, $notused); if ($count == 0) { // Nothing to do. return null; } else { // Extract the CAS commands. $temp = stack_utils::all_substring_between($this->trimmedcastext, '@', '@', true); // Create array of commands matching with their labels. $i = 0; $valid = true; $errors = ''; $cmdarray = array(); $labels = array(); $sessionkeys = array(); if (is_a($this->session, 'stack_cas_session')) { $sessionkeys = $this->session->get_all_keys(); } foreach ($temp as $cmd) { // Trim of surrounding white space and CAS commands. $cmd = stack_utils::trim_commands($cmd); $cs = new stack_cas_casstring($cmd); $cs->get_valid($this->security, $this->syntax, $this->insertstars); do { // ... make sure names are not already in use. $key = 'caschat' . $i; $i++; } while (in_array($key, $sessionkeys)); $sesionkeys[] = $key; $labels[] = $key; $cs->set_key($key, true); $cmdarray[] = $cs; $valid = $valid && $cs->get_valid(); $errors .= $cs->get_errors(); } if (!$valid) { $this->valid = false; $this->errors .= stack_string('stackCas_invalidCommand') . '</br>' . $errors; } if (!empty($cmdarray)) { $newsession = $this->session; if (null === $newsession) { $newsession = new stack_cas_session($cmdarray, null, $this->seed); } else { $newsession->add_vars($cmdarray); } $this->session = $newsession; // Now replace the commannds with their labels in the text. $this->trimmedcastext = stack_utils::replace_between($this->trimmedcastext, '@', '@', $labels, true); } } }