* @package tao */ class tao_models_classes_Parser { /** * XML content string * * @access protected * @var string */ protected $content = null; /** * Short description of attribute source * * @access protected * @var string */ protected $source = ''; /** * Short description of attribute sourceType * * @access protected * @var int */ protected $sourceType = 0; /** * Short description of attribute errors * * @access protected * @var array */ protected $errors = []; /** * Short description of attribute valid * * @access protected * @var boolean */ protected $valid = false; /** * Short description of attribute fileExtension * * @access protected * @var string */ protected $fileExtension = 'xml'; /** * Short description of attribute SOURCE_FILE * * @access public * @var int */ const SOURCE_FILE = 1; /** * Short description of attribute SOURCE_URL * * @access public * @var int */ const SOURCE_URL = 2; /** * Short description of attribute SOURCE_STRING * * @access public * @var int */ const SOURCE_STRING = 3; /** * Current file is \oat\oatbox\filesystem\File object */ const SOURCE_FLYFILE = 4; /** * Short description of method __construct * * @access public * @author Bertrand Chevrier, * @param string $source * @param array $options * @throws common_exception_Error * @throws \common_Exception * @throws \oat\oatbox\service\ServiceNotFoundException */ public function __construct($source, $options = []) { $sourceType = false; if ($source instanceof \oat\oatbox\filesystem\File) { $sourceType = self::SOURCE_FLYFILE; } elseif (is_string($source)) { if (preg_match("/^<\?xml(.*)?/m", trim($source))) { $sourceType = self::SOURCE_STRING; } elseif (preg_match("/^http/", $source)) { $sourceType = self::SOURCE_URL; } elseif (is_file($source)) { $sourceType = self::SOURCE_FILE; } else { $uploadFile = ServiceManager::getServiceManager()->get(UploadService::SERVICE_ID)->universalizeUpload($source); if ($uploadFile instanceof \oat\oatbox\filesystem\File) { $sourceType = self::SOURCE_FLYFILE; $source = $uploadFile; } } } if ($sourceType === false) { throw new common_exception_Error("Denied content in the source parameter! " . get_class($this) . " accepts either XML content, a URL to an XML Content or the path to a file but got " . substr($source, 0, 500)); } $this->sourceType = $sourceType; $this->source = $source; if (isset($options['extension'])) { $this->fileExtension = $options['extension']; } } public function getSource() { return $this->source; } /** * Short description of method validate * * @access public * @author Bertrand Chevrier, * @param string schema * @return boolean */ public function validate($schema = '') { //You know sometimes you think you have enough time, but it is not always true ... //(timeout in hudson with the generis-hard test suite) helpers_TimeOutHelper::setTimeOutLimit(helpers_TimeOutHelper::MEDIUM); $content = $this->getContent(); if (!empty($content)) { try { libxml_use_internal_errors(true); $dom = new DomDocument(); $dom->formatOutput = true; $dom->preserveWhiteSpace = false; $this->valid = $dom->loadXML($content); if ($this->valid && !empty($schema)) { $this->valid = $dom->schemaValidate($schema); } if (!$this->valid) { $this->addErrors(libxml_get_errors()); } libxml_clear_errors(); } catch (DOMException $de) { $this->addError($de); } } helpers_TimeOutHelper::reset(); return (bool) $this->valid; } /** * Excecute parser validation and stops at the first valid one, and returns the identified schema * * @param array $xsds * @return string */ public function validateMultiple($xsds = []) { $returnValue = ''; foreach ($xsds as $xsd) { $this->errors = []; if ($this->validate($xsd)) { $returnValue = $xsd; break; } } return $returnValue; } /** * Short description of method isValid * * @access public * @author Bertrand Chevrier, * @return boolean */ public function isValid() { return (bool) $this->valid; } /** * Short description of method getErrors * * @access public * @author Bertrand Chevrier, * @return array */ public function getErrors() { $returnValue = $this->errors; return (array) $returnValue; } /** * Short description of method displayErrors * * @access public * @author Bertrand Chevrier, * @param boolean htmlOutput * @return string */ public function displayErrors($htmlOutput = true) { $returnValue = (string) ''; foreach ($this->errors as $error) { $returnValue .= $error['message']; if (isset($error['file']) && isset($error['line'])) { $returnValue .= ' in file ' . $error['file'] . ', line ' . $error['line']; } $returnValue .= PHP_EOL; } if ($htmlOutput) { $returnValue = nl2br($returnValue); } return (string) $returnValue; } /** * Short description of method addError * * @access protected * @author Bertrand Chevrier, * @param mixed error * @return mixed */ protected function addError($error) { $this->valid = false; if ($error instanceof Exception) { $this->errors[] = [ 'file' => $error->getFile(), 'line' => $error->getLine(), 'message' => "[" . get_class($error) . "] " . $error->getMessage() ]; } elseif ($error instanceof LibXMLError) { $this->errors[] = [ 'file' => $error->file, 'line' => $error->line, 'message' => "[" . get_class($error) . "] " . $error->message ]; } elseif (is_string($error)) { $this->errors[] = [ 'message' => $error ]; } } /** * Get XML content. * * @access protected * @author Aleh Hutnikau, * @param boolean $refresh load content again. * @return string */ public function getContent($refresh = false) { if ($this->content === null || $refresh) { try { switch ($this->sourceType) { case self::SOURCE_FILE: //check file if (!file_exists($this->source)) { throw new Exception("File {$this->source} not found."); } if (!is_readable($this->source)) { throw new Exception("Unable to read file {$this->source}."); } if (!preg_match("/\.{$this->fileExtension}$/", basename($this->source))) { throw new Exception("Wrong file extension in " . basename($this->source) . ", {$this->fileExtension} extension is expected"); } if (!tao_helpers_File::securityCheck($this->source)) { throw new Exception("{$this->source} seems to contain some security issues"); } $this->content = file_get_contents($this->source); break; case self::SOURCE_URL: //only same domain if (!preg_match("/^" . preg_quote(BASE_URL, '/') . "/", $this->source)) { throw new Exception("The given uri must be in the domain {$_SERVER['HTTP_HOST']}"); } $this->content = tao_helpers_Request::load($this->source, true); break; case self::SOURCE_STRING: $this->content = $this->source; break; case self::SOURCE_FLYFILE: if (! $this->source->exists()) { throw new Exception('Source file does not exists ("' . $this->source->getBasename() . '").'); } if (! $this->content = $this->source->read()) { throw new Exception('Unable to read file ("' . $this->source->getBasename() . '").'); } break; } } catch (Exception $e) { $this->addError($e); } } return $this->content; } /** * Short description of method addErrors * * @access protected * @author Bertrand Chevrier, * @param array errors * @return mixed */ protected function addErrors($errors) { foreach ($errors as $error) { $this->addError($error); } } /** * Short description of method clearErrors * * @access protected * @author Bertrand Chevrier, * @return mixed */ protected function clearErrors() { $this->errors = []; } /** * Creates a report without title of the parsing result * @return common_report_Report */ public function getReport() { if ($this->isValid()) { return common_report_Report::createSuccess(''); } else { $report = new common_report_Report(''); foreach ($this->getErrors() as $error) { $report->add(common_report_Report::createFailure($error['message'])); } return $report; } } }