385 lines
12 KiB
PHP
385 lines
12 KiB
PHP
<?php
|
|
define('VAL_ROOT', 110);
|
|
define('VAL_NOREQE', 111);
|
|
define('VAL_NOONEOF', 112);
|
|
define('VAL_UNKNOWNE', 113);
|
|
define('VAL_UNKNOWNA', 114);
|
|
define('VAL_NOTDEF', 115);
|
|
define('VAL_UNEXPONEOF', 116);
|
|
define('VAL_FORMAT', 117);
|
|
define('VAL_CONTENT', 118);
|
|
define('VAL_NOREQA', 119);
|
|
define('VAL_ATTRIB', 120);
|
|
define('VAL_PREDXML', 121);
|
|
|
|
/**
|
|
* Simple XML validator against structure stored in PHP hash-array hierarchy.
|
|
*
|
|
* Basic format files:
|
|
* <ul>
|
|
* <li>audioClipFormat.php</li>
|
|
* <li>webstreamFormat.php</li>
|
|
* <li>playlistFormat.php</li>
|
|
* </ul>
|
|
* It probably should be replaced by XML schema validation in the future.
|
|
*
|
|
* @package Campcaster
|
|
* @subpackage StorageServer
|
|
* @copyright 2010 Sourcefabric O.P.S.
|
|
* @license http://www.gnu.org/licenses/gpl.txt
|
|
*/
|
|
class Validator {
|
|
/**
|
|
* Format type of validated document
|
|
* @var string
|
|
*/
|
|
private $format = NULL;
|
|
|
|
/**
|
|
* Preloaded format tree structure
|
|
* @var array
|
|
*/
|
|
private $formTree = NULL;
|
|
|
|
/**
|
|
* Gunid of validated file for identification in mass input
|
|
* @var string
|
|
*/
|
|
private $gunid = NULL;
|
|
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param string $format
|
|
* format type of validated document
|
|
* @param string $gunid
|
|
* gunid of validated file for identification in mass input
|
|
*/
|
|
public function __construct($format, $gunid)
|
|
{
|
|
$format = strtolower($format);
|
|
$this->format = $format;
|
|
$this->gunid = $gunid;
|
|
$formats = array(
|
|
'audioclip' => "audioClipFormat",
|
|
'playlist' => "playlistFormat",
|
|
'webstream' => "webstreamFormat",
|
|
);
|
|
if (!isset($formats[$format])) {
|
|
return $this->_err(VAL_FORMAT);
|
|
}
|
|
$formatName = $formats[$format];
|
|
$formatFile = dirname(__FILE__)."/$formatName.php";
|
|
if (!file_exists($formatFile)) {
|
|
return $this->_err(VAL_FORMAT);
|
|
}
|
|
require($formatFile);
|
|
$this->formTree = $$formatName;
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate document - only wrapper for validateNode method
|
|
*
|
|
* @param object $data
|
|
* validated object tree
|
|
* @return mixed
|
|
* TRUE or PEAR::error
|
|
*/
|
|
function validate(&$data)
|
|
{
|
|
$r = $this->validateNode($data, $this->formTree['_root']);
|
|
return $r;
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate one metadata value (on insert/update)
|
|
*
|
|
* @param string $fname
|
|
* parent element name
|
|
* @param string $category
|
|
* qualif.category name
|
|
* @param string $predxml
|
|
* 'A' | 'T' (attr or tag)
|
|
* @param string $value
|
|
* validated element value
|
|
* @return TRUE|PEAR_Error
|
|
*/
|
|
function validateOneValue($fname, $category, $predxml, $value)
|
|
{
|
|
$formTree =& $this->formTree;
|
|
switch ($predxml) {
|
|
case 'T':
|
|
if (!$this->isChildInFormat($fname, $category)) {
|
|
return $this->_err(VAL_UNKNOWNE, "$category in $fname");
|
|
}
|
|
break;
|
|
case 'A':
|
|
if (!$this->isAttrInFormat($fname, $category)) {
|
|
return $this->_err(VAL_UNKNOWNA, "$category in $fname");
|
|
}
|
|
break;
|
|
case 'N':
|
|
return TRUE;
|
|
break;
|
|
default:
|
|
return $this->_err(VAL_PREDXML, $predxml);
|
|
}
|
|
if (isset($formTree[$category]['regexp'])) {
|
|
// echo "XXX {$formTree[$fname]['regexp']} / ".$node->content."\n";
|
|
if (!preg_match("|{$formTree[$category]['regexp']}|", $value)) {
|
|
return $this->_err(VAL_CONTENT, "$category/$value");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Validation of one element node from object tree
|
|
*
|
|
* @param object $node
|
|
* validated node
|
|
* @param string $fname
|
|
* actual name in format structure
|
|
* @return mixed
|
|
* TRUE or PEAR::error
|
|
*/
|
|
function validateNode(&$node, $fname)
|
|
{
|
|
$dname = (($node->ns? $node->ns.":" : '').$node->name);
|
|
$formTree =& $this->formTree;
|
|
if (DEBUG) {
|
|
echo"\nVAL::validateNode: 1 $dname/$fname\n";
|
|
}
|
|
// check root node name:
|
|
if ($dname != $fname) {
|
|
return $this->_err(VAL_ROOT, $fname);
|
|
}
|
|
// check if this element is defined in format:
|
|
if (!isset($formTree[$fname])) {
|
|
return $this->_err(VAL_NOTDEF, $fname);
|
|
}
|
|
// check element content
|
|
if (isset($formTree[$fname]['regexp'])) {
|
|
// echo "XXX {$formTree[$fname]['regexp']} / ".$node->content."\n";
|
|
if (!preg_match("|{$formTree[$fname]['regexp']}|", $node->content)) {
|
|
return $this->_err(VAL_CONTENT, "$fname/{$node->content}");
|
|
}
|
|
}
|
|
// validate attributes:
|
|
$ra = $this->validateAttributes($node, $fname);
|
|
if (PEAR::isError($ra)) {
|
|
return $ra;
|
|
}
|
|
// validate children:
|
|
$r = $this->validateChildren($node, $fname);
|
|
if (PEAR::isError($r)) {
|
|
return $r;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Validation of attributes
|
|
*
|
|
* @param object $node
|
|
* validated node
|
|
* @param string $fname
|
|
* actual name in format structure
|
|
* @return mixed
|
|
* TRUE or PEAR::error
|
|
*/
|
|
function validateAttributes(&$node, $fname)
|
|
{
|
|
$formTree =& $this->formTree;
|
|
$attrs = array();
|
|
// check if all attrs are permitted here:
|
|
foreach ($node->attrs as $i => $attr) {
|
|
$aname = (($attr->ns? $attr->ns.":" : '').$attr->name);
|
|
$attrs[$aname] =& $node->attrs[$i];
|
|
if (!$this->isAttrInFormat($fname, $aname)) {
|
|
return $this->_err(VAL_UNKNOWNA, $aname);
|
|
}
|
|
// check attribute format
|
|
// echo "XXA $aname\n";
|
|
if (isset($formTree[$aname]['regexp'])) {
|
|
// echo "XAR {$formTree[$fname]['regexp']} / ".$node->content."\n";
|
|
if (!preg_match("|{$formTree[$aname]['regexp']}|", $attr->val)) {
|
|
return $this->_err(VAL_ATTRIB, "$aname [".var_export($attr->val,TRUE)."]");
|
|
}
|
|
}
|
|
}
|
|
// check if all required attrs are here:
|
|
if (isset($formTree[$fname]['attrs'])) {
|
|
$fattrs =& $formTree[$fname]['attrs'];
|
|
if (isset($fattrs['required'])) {
|
|
foreach ($fattrs['required'] as $i => $attr) {
|
|
if (!isset($attrs[$attr])) {
|
|
return $this->_err(VAL_NOREQA, $attr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Validation children nodes
|
|
*
|
|
* @param object $node
|
|
* validated node
|
|
* @param string $fname
|
|
* actual name in format structure
|
|
* @return mixed
|
|
* TRUE or PEAR::error
|
|
*/
|
|
function validateChildren(&$node, $fname)
|
|
{
|
|
$formTree =& $this->formTree;
|
|
$childs = array();
|
|
// check if all children are permitted here:
|
|
foreach ($node->children as $i => $ch) {
|
|
$chname = (($ch->ns? $ch->ns.":" : '').$ch->name);
|
|
// echo "XXE $chname\n";
|
|
if (!$this->isChildInFormat($fname, $chname)) {
|
|
return $this->_err(VAL_UNKNOWNE, $chname);
|
|
}
|
|
// call children recursive:
|
|
$r = $this->validateNode($node->children[$i], $chname);
|
|
if (PEAR::isError($r)) {
|
|
return $r;
|
|
}
|
|
$childs[$chname] = TRUE;
|
|
}
|
|
// check if all required children are here:
|
|
if (isset($formTree[$fname]['childs'])) {
|
|
$fchilds =& $formTree[$fname]['childs'];
|
|
if (isset($fchilds['required'])) {
|
|
foreach ($fchilds['required'] as $i => $ch) {
|
|
if (!isset($childs[$ch])) return $this->_err(VAL_NOREQE, $ch);
|
|
}
|
|
}
|
|
// required one from set
|
|
if (isset($fchilds['oneof'])) {
|
|
$one = FALSE;
|
|
foreach ($fchilds['oneof'] as $i => $ch) {
|
|
if (isset($childs[$ch])) {
|
|
if ($one) {
|
|
return $this->_err(VAL_UNEXPONEOF, "$ch in $fname");
|
|
}
|
|
$one = TRUE;
|
|
}
|
|
}
|
|
if (!$one) {
|
|
return $this->_err(VAL_NOONEOF);
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Test if child is presented in format structure
|
|
*
|
|
* @param string $fname
|
|
* node name in format structure
|
|
* @param string $chname
|
|
* child node name
|
|
* @return boolean
|
|
*/
|
|
function isChildInFormat($fname, $chname)
|
|
{
|
|
$listo = $this->isInFormatAs($fname, $chname, 'childs', 'optional');
|
|
$listr = $this->isInFormatAs($fname, $chname, 'childs', 'required');
|
|
$list1 = $this->isInFormatAs($fname, $chname, 'childs', 'oneof');
|
|
return ($listo!==FALSE || $listr!==FALSE || $list1!==FALSE);
|
|
}
|
|
|
|
|
|
/**
|
|
* Test if attribute is presented in format structure
|
|
*
|
|
* @param string $fname
|
|
* node name in format structure
|
|
* @param string $aname
|
|
* attribute name
|
|
* @return boolean
|
|
*/
|
|
function isAttrInFormat($fname, $aname)
|
|
{
|
|
$listr = $this->isInFormatAs($fname, $aname, 'attrs', 'required');
|
|
$listi = $this->isInFormatAs($fname, $aname, 'attrs', 'implied');
|
|
$listn = $this->isInFormatAs($fname, $aname, 'attrs', 'normal');
|
|
return ($listr!==FALSE || $listi!==FALSE || $listn!==FALSE);
|
|
}
|
|
|
|
|
|
/**
|
|
* Check if node/attribute is presented in format structure
|
|
*
|
|
* @param string $fname
|
|
* node name in format structure
|
|
* @param string $chname
|
|
* node/attribute name
|
|
* @param string $nType
|
|
* 'childs' | 'attrs'
|
|
* @param string $reqType
|
|
* <ul>
|
|
* <li>for elements: 'required' | 'optional' | 'oneof'</li>
|
|
* <li>for attributes: 'required' | 'implied' | 'normal'</li>
|
|
* </ul>
|
|
* @return mixed
|
|
* boolean/int (index int format array returned if found)
|
|
*/
|
|
function isInFormatAs($fname, $chname, $nType='childs', $reqType='required')
|
|
{
|
|
$formTree =& $this->formTree;
|
|
$listed = (
|
|
isset($formTree[$fname][$nType][$reqType]) ?
|
|
array_search($chname, $formTree[$fname][$nType][$reqType]) :
|
|
FALSE
|
|
);
|
|
return $listed;
|
|
}
|
|
|
|
|
|
/**
|
|
* Error exception generator
|
|
*
|
|
* @param int $errno
|
|
* erron code
|
|
* @param string $par
|
|
* optional string for more descriptive error messages
|
|
* @return PEAR_Error
|
|
*/
|
|
function _err($errno, $par='')
|
|
{
|
|
$msg = array(
|
|
VAL_ROOT => 'Wrong root element',
|
|
VAL_NOREQE => 'Required element missing',
|
|
VAL_NOONEOF => 'One-of element missing',
|
|
VAL_UNKNOWNE => 'Unknown element',
|
|
VAL_UNKNOWNA => 'Unknown attribute',
|
|
VAL_NOTDEF => 'Not defined',
|
|
VAL_UNEXPONEOF => 'Unexpected second object from one-of set',
|
|
VAL_FORMAT => 'Unknown format',
|
|
VAL_CONTENT => 'Invalid content',
|
|
VAL_NOREQA => 'Required attribute missing',
|
|
VAL_ATTRIB => 'Invalid attribute format',
|
|
VAL_PREDXML => 'Invalid predicate type',
|
|
);
|
|
return PEAR::raiseError(
|
|
"Validator: {$msg[$errno]} #$errno ($par, gunid={$this->gunid})",
|
|
$errno
|
|
);
|
|
}
|
|
|
|
|
|
} // class Validator
|
|
|
|
?>
|