/** * Set the QTI Data Model Rule object to be processed. * * @param \qtism\runtime\rules\Rule $rule * @throws \InvalidArgumentException If $rule is not compliant with the rule processor implementation. */ public function setRule(Rule $rule) { $expectedType = $this->getRuleType(); if (Reflection::isInstanceOf($rule, $expectedType) === true) { $this->rule = $rule; } else { $procClass = get_class($this); $givenType = get_class($rule); $msg = "The {$procClass} Rule Processor only processes {$expectedType} Rule objects, {$givenType} given."; throw new InvalidArgumentException($msg); } }
/** * Set the QTI Data Model Expression to be processed. * * @param \qtism\data\expressions\Expression $expression A QTI Data Model Expression object. * @throws \InvalidArgumentException If $expression is not a subclass nor implements the Expression type returned by the getExpressionType method. */ public function setExpression(Expression $expression) { $expectedType = $this->getExpressionType(); if (ReflectionUtils::isInstanceOf($expression, $expectedType) === true) { $this->expression = $expression; } else { $procClass = get_class($this); $givenType = get_class($expression); $msg = "The {$procClass} Expression Processor only processes {$expectedType} Expression objects, {$givenType} given."; throw new InvalidArgumentException($msg); } $this->expression = $expression; }
/** * Process the setOutcomeValue/setTemplateValue rule. * * A RuleProcessingException will be thrown if: * * * The variable does not exist. * * The requested variable is not an OutcomeVariable/TemplateVariable. * * The variable's baseType does not match the baseType of the affected value. * * An error occurs while processing the related expression. * * @throws \qtism\runtime\rules\RuleProcessingException If one of the error described above arise. */ public function process() { $state = $this->getState(); $rule = $this->getRule(); $identifier = $rule->getIdentifier(); $var = $state->getVariable($identifier); if (is_null($var) === true) { $msg = "No variable with identifier '{$identifier}' to be set in the current state."; throw new RuleProcessingException($msg, $this, RuleProcessingException::NONEXISTENT_VARIABLE); } elseif (Reflection::isInstanceOf($var, $this->getVariableType()) === false) { $msg = "The variable to set '{$identifier}' is not an instance of '" . $this->getVariableType() . "'."; throw new RuleProcessingException($msg, $this, RuleProcessingException::WRONG_VARIABLE_TYPE); } // Process the expression. // Its result will be the value to set to the target variable. try { $expressionEngine = new ExpressionEngine($rule->getExpression(), $state); $val = $expressionEngine->process(); } catch (ExpressionProcessingException $e) { $msg = "An error occured while processing the expression bound with the '" . Reflection::shortClassName($rule) . "' rule."; throw new RuleProcessingException($msg, $this, RuleProcessingException::RUNTIME_ERROR, $e); } // The variable exists, its new value is processed. try { // juggle a little bit (int -> float, float -> int) if ($val !== null && $var->getCardinality() === Cardinality::SINGLE) { $baseType = $var->getBaseType(); if ($baseType === BaseType::INTEGER && $val instanceof Float) { $val = new Integer(intval($val->getValue())); } elseif ($baseType === BaseType::FLOAT && $val instanceof Integer) { $val = new Float(floatval($val->getValue())); } } $var->setValue($val); } catch (InvalidArgumentException $e) { $varBaseType = BaseType::getNameByConstant($var->getBaseType()) === false ? 'noBaseType' : BaseType::getNameByConstant($var->getBaseType()); $varCardinality = Cardinality::getNameByConstant($var->getCardinality()); // The affected value does not match the baseType of the variable $var. $msg = "Unable to set value {$val} to variable '{$identifier}' (cardinality = {$varCardinality}, baseType = {$varBaseType})."; throw new RuleProcessingException($msg, $this, RuleProcessingException::WRONG_VARIABLE_BASETYPE, $e); } }
/** * @see \qtism\data\storage\xml\marshalling\RecursiveMarshaller::unmarshallChildrenKnown() */ protected function unmarshallChildrenKnown(DOMElement $element, QtiComponentCollection $children) { $expressionElts = self::getChildElementsByTagName($element, Expression::getExpressionClassNames()); if (count($expressionElts) > 0) { $marshaller = $this->getMarshallerFactory()->createMarshaller($expressionElts[0]); $expression = $marshaller->unmarshall($expressionElts[0]); } elseif (($element->localName == 'responseIf' || $element->localName == 'responseElseIf') && count($expressionElts) == 0) { $msg = "A '" . $element->localName . "' must contain an 'expression' element. None found at line " . $element->getLineNo() . "'."; throw new UnmarshallingException($msg, $element); } if ($element->localName == 'responseIf' || $element->localName == 'responseElseIf') { $className = 'qtism\\data\\rules\\' . ucfirst($element->localName); $class = new ReflectionClass($className); $object = Reflection::newInstance($class, array($expression, $children)); } else { $className = 'qtism\\data\\rules\\' . ucfirst($element->localName); $class = new ReflectionClass($className); $object = Reflection::newInstance($class, array($children)); } return $object; }
protected function instantiateMarshaller(ReflectionClass $class, array $args) { array_unshift($args, '2.0.0'); return Reflection::newInstance($class, $args); }
/** * Returns a processing error reporting message in the following format: * * [ExpressionClassName] My message... * * @param \qtism\data\expressions\Expression $expression A given expression that failed to be processed. * @param string $message A formatted error reporting message. * @return string */ public static function errorReporting(Expression $expression, $message) { $shortClassName = Reflection::shortClassName($expression); return "[{$shortClassName}] {$message}"; }
/** * @see \qtism\data\storage\xml\marshalling\RecursiveMarshaller::unmarshallChildrenKnown() */ protected function unmarshallChildrenKnown(DOMElement $element, QtiComponentCollection $children) { // Some exceptions applies on instanciation e.g. the And operator is named // AndOperator because of PHP reserved words restriction. if ($element->localName === 'and') { $className = 'qtism\\data\\expressions\\operators\\AndOperator'; } elseif ($element->localName === 'or') { $className = 'qtism\\data\\expressions\\operators\\OrOperator'; } else { $className = 'qtism\\data\\expressions\\operators\\' . ucfirst($element->localName); } $class = new ReflectionClass($className); $params = array($children); if ($element->localName === 'customOperator') { // Retrieve XML content as a string. $frag = $element->ownerDocument->createDocumentFragment(); $element = $element->cloneNode(true); $frag->appendChild($element); $params[] = $frag->ownerDocument->saveXML($frag); $component = Reflection::newInstance($class, $params); if (($class = self::getDOMElementAttributeAs($element, 'class')) !== null) { $component->setClass($class); } if (($definition = self::getDOMElementAttributeAs($element, 'definition')) !== null) { $component->setDefinition($definition); } return $component; } else { return Reflection::newInstance($class, $params); } }
public function testNewInstanceWithoutArguments() { $clazz = new ReflectionClass('\\stdClass'); $instance = Reflection::newInstance($clazz); $this->assertInstanceOf('\\stdClass', $instance); }
/** * Create a marshaller for a given QtiComponent or DOMElement object, depending on the current mapping * of the MarshallerFactory. If no mapping entry can be found, the factory will perform a ultimate * trial in the qtism\\data\\storage\\xml\\marshalling namespace to find the relevant Marshaller object. * * The newly created marshaller will be set up with the MarshallerFactory itself as its MarshallerFactory * object (yes, we know, this is highly recursive but necessary x)). * * @param DOMElement|QtiComponent $object A QtiComponent or DOMElement object you want to get the corresponding Marshaller object. * @param array $args An optional array of arguments to be passed to the Marshaller constructor. * @throws InvalidArgumentException If $object is not a QtiComponent nor a DOMElement object. * @throws RuntimeException If no Marshaller object can be created for the given $object. * @return Marshaller The corresponding Marshaller object. */ public function createMarshaller($object, array $args = array()) { if ($object instanceof QtiComponent) { $qtiClassName = $object->getQtiClassName(); } else { if ($object instanceof DOMElement) { $qtiClassName = $object->localName; } } if (isset($qtiClassName)) { try { // Look for a mapping entry. if ($this->hasMappingEntry($qtiClassName)) { $class = new ReflectionClass($this->getMappingEntry($qtiClassName)); } else { // Look for default. $className = 'qtism\\data\\storage\\xml\\marshalling\\' . ucfirst($qtiClassName) . 'Marshaller'; $class = new ReflectionClass($className); } } catch (ReflectionException $e) { $msg = "No marshaller implementation could be found for component '{$qtiClassName}'."; throw new RuntimeException($msg, 0, $e); } $marshaller = Reflection::newInstance($class, $args); $marshaller->setMarshallerFactory($this); return $marshaller; } else { $msg = "The object argument must be a QtiComponent or a DOMElementObject, '" . gettype($object) . "' given."; throw new InvalidArgumentException($msg); } }
/** * @dataProvider shortClassNameProvider * @param mixed $expected * @param mixed $object */ public function testShortClassName($expected, $object) { $this->assertSame($expected, Reflection::shortClassName($object)); }