/**
  * 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);
         }
     }
 }