tao-test/app/tao/models/classes/class.Parser.php

415 lines
12 KiB
PHP

<?php
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2002-2008 (original work) Public Research Centre Henri Tudor & University of Luxembourg (under the project TAO & TAO2);
* 2008-2010 (update and modification) Deutsche Institut für Internationale Pädagogische Forschung (under the project TAO-TRANSFER);
* 2009-2012 (update and modification) Public Research Centre Henri Tudor (under the project TAO-SUSTAIN & TAO-DEV);
*
*/
use oat\oatbox\service\ServiceManager;
use oat\tao\model\upload\UploadService;
/**
* The Parser enables you to load, parse and validate xml content from an xml
* Usually used for to load and validate the itemContent property.
*
* @access public
* @author Bertrand Chevrier, <bertrand.chevrier@tudor.lu>
* @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, <bertrand.chevrier@tudor.lu>
* @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, <bertrand.chevrier@tudor.lu>
* @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, <bertrand.chevrier@tudor.lu>
* @return boolean
*/
public function isValid()
{
return (bool) $this->valid;
}
/**
* Short description of method getErrors
*
* @access public
* @author Bertrand Chevrier, <bertrand.chevrier@tudor.lu>
* @return array
*/
public function getErrors()
{
$returnValue = $this->errors;
return (array) $returnValue;
}
/**
* Short description of method displayErrors
*
* @access public
* @author Bertrand Chevrier, <bertrand.chevrier@tudor.lu>
* @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, <bertrand.chevrier@tudor.lu>
* @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, <hutnikau@1pt.com>
* @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, <bertrand.chevrier@tudor.lu>
* @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, <bertrand.chevrier@tudor.lu>
* @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;
}
}
}