<?php
/**
* Copyright (C) 2011 Kai Dorschner
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* @author Kai Dorschner <the-hide@address.com>
* @copyright Copyright 2011, Kai Dorschner
* @license http://www.gnu.org/licenses/gpl.html GPLv3
* @package mocovi
* @subpackage filesystems
*
*
*
* @todo RelaxNG doesn't work properly on libxml < 2.7.6
*/
/**
* Require Filesystem.
*/
class_exists('XMLFS') or require $GLOBALS['filesystemsLibrary'].'XMLFS.php';
/**
* This class is used when an operation is not allowed.
*/
class NotAllowedException extends Exception {}
/**
* Writable XML Filesystem
*
* This filesystem consists of XML and contains all files and each model of a file.
* The whole content can be edited.
*
* @package mocovi
* @subpackage filesystems
*/
class WritableXMLFS extends XMLFS
{
public function deepCreateFile($path)
{
if(substr($path, 0, 1) != '/')
throw new Exception('File path must be absolute!');
$breadcrumbs = explode('/', trim($path, '/'));
$build = '/';
$parent = $this->getDOM()->documentElement;
for($i = 0; $i < count($breadcrumbs); $i++)
{
if(!$this->isFile($build .= $breadcrumbs[$i].'/'))
$parent->appendChild($this->_createFile($breadcrumbs[$i]));
$parent = $this->getFile($build);
}
return $this->file($path);
}
/**
* This file is not appended anywhere when created.
*
*/
public function newFile($name)
{
return new WritableXMLFSFile($this->_createFile($name), $this);
}
public function file($path)
{
return new WritableXMLFSFile($this->getFile($path), $this);
}
public function save()
{
$this->dom->preserveWhitespace = false;
$this->dom->formatOutput = true;
return file_put_contents
( $this->source
, preg_replace('/[ ]{2}/', "\t", $this->dom->saveXML()) // replace 2 whitespaces with one tab
, LOCK_EX // Prevent anyone else writing to the file at the same time
);
}
public function saveAs($path)
{
// @todo implement
}
protected function _createFile($name)
{
$new = $this->getDOM()->createElementNS(Mocovi::$namespace, 'file');
$new->setAttribute('name', str_replace(array(' ', '.', ',', '/'), '_', $name));
$new->setAttribute('created', date('c'));
$new->appendChild($model = $this->getDOM()->createElementNS(Mocovi::$namespace, 'model'));
$model->appendChild($root = $this->getDOM()->createElementNS(Mocovi::$namespace, 'root'));
$root->appendChild($this->getDOM()->createElementNS(Mocovi::$namespace, 'header'));
$root->appendChild($this->getDOM()->createElementNS(Mocovi::$namespace, 'menu'));
$root->appendChild($content = $this->getDOM()->createElementNS(Mocovi::$namespace, 'article'));
$content->appendChild($this->getDOM()->createElementNS(Mocovi::$namespace, 'headline'));
$content->appendChild($this->getDOM()->createElementNS(Mocovi::$namespace, 'paragraph'));
$root->appendChild($this->getDOM()->createElementNS(Mocovi::$namespace, 'footer'));
return $new;
}
}
class WritableXMLFSFile
{
protected $node;
protected $filesystem;
public function __construct(DomNode $node, XMLFS $filesystem)
{
$this->node = $node;
$this->filesystem = $filesystem;
}
public function control($xpath)
{
$reference = $this->node;
if($node = $this->filesystem->xpath->query('mocovi:model'.(substr($xpath, 0, 1) != '/' ?'/' : '').$xpath, $reference)->item(0))
if($node->nodeType === XML_TEXT_NODE)
return new WritableXMLFSControlText($node, $this, $this->filesystem->xpath);
else
return new WritableXMLFSControl($node, $this, $this->filesystem->xpath);
else
throw new Exception('Control for xpath '.$xpath.' not found in file '.$this->getName());
}
/**
* This control is not appended anywhere when created.
*
*/
public function newControl($name)
{
if($name == '#text')
{
return new WritableXMLFSControlText
( $this
->filesystem
->getDOM()
->createTextNode('continuous text...')
, $this
, $this->filesystem->xpath
);
}
return new WritableXMLFSControl
( $this
->filesystem
->getDOM()
->createElementNS(Mocovi::$namespace, $name)
, $this
, $this->filesystem->xpath
);
}
public function getNameAlias()
{
return $this->node->getAttribute('nameAlias');
}
public function getNameAliasToken()
{
return $this->node->getAttribute('nameAliasToken');
}
public function setNameAlias($alias)
{
$this->node->setAttribute('nameAlias', $alias);
return $this;
}
public function setNameAliasToken($aliasToken)
{
$this->node->setAttribute('nameAliasToken', $aliasToken);
return $this;
}
public function canMove($path)
{
return true;
}
public function move($path)
{
if(!$this->canMove($path))
throw new NotAllowedException($path);
$this->filesystem->getFile($path)->appendChild($this->node);
return $this;
}
public function canMoveBefore($path)
{
return true;
}
public function moveBefore($path)
{
if(!$this->canMoveBefore($path))
throw new NotAllowedException($path);
$target = $this->filesystem->getFile($path);
$target->parentNode->insertBefore($this->node, $target);
return $this;
}
public function canMoveAfter($path)
{
return true;
}
public function moveAfter($path)
{
if(!$this->canMoveAfter($path))
throw new NotAllowedException($path);
$target = $this->filesystem->getFile($path);
if($target->nextSibling)
$target->parentNode->insertBefore
( $this->node
, $target->nextSibling
);
else
$target->parentNode->appendChild($this->node);
return $this;
}
public function canCopy($path)
{
return true;
}
public function copy($path)
{
if(!$this->canCopy($path))
throw new NotAllowedException($path);
$this->filesystem->getFile($path)->appendChild($new = $this->cloneNode());
return new WritableXMLFSFile($new, $this->filesystem);
}
public function canCopyBefore($path)
{
return true;
}
public function copyBefore($path)
{
if(!$this->canCopyBefore($path))
throw new NotAllowedException($path);
$target = $this->filesystem->getFile($path);
$target->parentNode->insertBefore($new = $this->cloneNode(), $target);
return new WritableXMLFSFile($new, $this->filesystem);
}
public function canCopyAfter($path)
{
return true;
}
public function copyAfter($path)
{
if(!$this->canCopyAfter($path))
throw new NotAllowedException($path);
$new = $this->cloneNode();
$target = $this->filesystem->getFile($path);
if($target->nextSibling)
$target->parentNode->insertBefore
( $new
, $target->nextSibling
);
else
$target->parentNode->appendChild($new);
return new WritableXMLFSFile($new, $this->filesystem);
}
public function canReplace($path)
{
return true;
}
public function replace($path)
{
if(!$this->canReplace($path))
throw new NotAllowedException($path);
$target = $this->filesystem->getFile($path);
$target->parentNode->replaceChild($this->node, $target);
return $this;
}
public function canRename($newname)
{
return true;
}
public function rename($newname)
{
if(!$this->canRename($path))
throw new NotAllowedException($path);
$this->setAttribute('name', $newname);
}
public function clear()
{
while($this->node->childNodes->length > 0)
$this->node->removeChild($this->node->firstChild);
return $this;
}
public function delete()
{
$this->node->parentNode->removeChild($this->node);
unset($this);
}
public function makeVisible()
{
$this->node->removeAttribute('invisible');
return $this;
}
public function makeInvisible()
{
$this->setAttribute('invisible', '1');
return $this;
}
public function setAuthor($name)
{
$this->setAttribute('author', $name);
return $this;
}
public function updateLastModified()
{
$this->setAttribute('lastModified', date('c'));
return $this;
}
public function setRedirect($path)
{
if($this->filesystem->isFile($path))
$this->setAttribute('redirect', $path);
else
throw new Exception($path.' not found for redirect.');
return $this;
}
public function removeRedirect()
{
$this->removeAttribute('redirect');
return $this;
}
public function getNode()
{
return $this->node;
}
public function getName()
{
return $this->node->getAttribute('name');
}
protected function cloneNode()
{
$clone = $this->node->cloneNode(true);
$clone->setAttribute('name', $clone->getAttribute('name').'_copy');
return $clone;
}
protected function setAttribute($name, $value)
{
$this->node->setAttribute($name, $value);
}
}
class WritableXMLFSControl
{
protected $namespace; // owner document
protected $dom; // owner document
protected $xpath; // filesystem XPath object
protected $node; // Current control node
protected $file; // Current file context
protected $test; // Current file context clone, for testing / validating
protected $control; // Current Control
protected $excludeNodes = array
( 'xi:include'
, 'xref:reference'
, '#comment'
);
protected $undeletableControls = array
( 'root'
, 'article'
);
/**
* This is only temporary. When the RelaxNG schema validation works fine,
* there is no need to include this.
* @deprecated
*/
protected $relevantControls = array
( '#text'
, 'anchor'
//, 'cite'
//, 'article'
, 'date'
, 'duration'
, 'element'
//, 'emphasized'
//, 'error'
//, 'feedreader'
//, 'fileanchor'
//, 'gravatar'
, 'headline'
, 'image'
, 'imagegallery'
, 'inline'
, 'listing'
, 'mediaplayer'
, 'overviewlist'
, 'paragraph'
//, 'param'
//, 'prefacetext'
, 'sitemap'
//, 'strike'
, 'strong'
, 'text'
//, 'toc'
, 'twitter'
, 'vimeo'
, 'youtube'
);
public function __construct(DomNode $node, WritableXMLFSFile $file, DomXpath $xpath)
{
if(in_array($node->nodeType, array(XML_ELEMENT_NODE, XML_TEXT_NODE)))
{
$name = $node->nodeName;
if(!self::controlExists($name))
throw new Exception('Control "'.$name.'" does not exist.');
$this->node = $node;
$this->file = $file;
$this->test = $file->getNode()->cloneNode(true); // Is needed to test modifications (move, delete, ...)!
$this->dom = $node->ownerDocument;
$this->xpath = $xpath;
$this->control = ControlFactory::createVirtual($name);
$this->namespace = Mocovi::$namespace;
}
}
public static function controlExists($name)
{
$x = ControlFactory::getAllControls();
return $x[$name];
}
public function canMove($xpath)
{
$this->_move($this->test, $this->node->cloneNode(true), $xpath);
return $this->testIsValid();
}
protected function _move(DomNode $context, DomNode $source, $xpath)
{
$this->findControl($context, $xpath)->appendChild($source);
}
public function move($xpath)
{
if(!$this->canMove($xpath))
throw new NotAllowedException($this->node->nodeName.': '.__FUNCTION__);
$this->_move($this->file->getNode(), $this->node, $xpath);
return $this;
}
public function canMoveBefore($xpath)
{
$this->_moveBefore($this->test, $this->node->cloneNode(true), $xpath);
return $this->testIsValid();
}
protected function _moveBefore(DomNode $context, DomNode $source, $xpath)
{
$target = $this->findControl($context, $xpath);
$target->parentNode->insertBefore($source, $target);
}
public function moveBefore($xpath)
{
if(!$this->canMoveBefore($xpath))
throw new NotAllowedException($this->node->nodeName.': '.__FUNCTION__);
$this->_moveBefore($this->file->getNode(), $this->node, $xpath);
return $this;
}
public function canMoveAfter($xpath)
{
$this->_moveAfter($this->test, $this->node->cloneNode(true), $xpath);
return $this->testIsValid();
}
protected function _moveAfter(DomNode $context, DomNode $source, $xpath)
{
$target = $this->findControl($context, $xpath);
if($target->nextSibling)
$target->parentNode->insertBefore
( $source
, $target->nextSibling
);
else
$target->parentNode->appendChild($source);
}
public function moveAfter($xpath)
{
if(!$this->canMoveAfter($xpath))
throw new NotAllowedException($this->node->nodeName.': '.__FUNCTION__);
$this->_moveAfter($this->file->getNode(), $this->node, $xpath);
return $this;
}
public function canCopy($xpath)
{
$this->_copy($this->test, $this->node->cloneNode(true), $xpath);
return $this->testIsValid();
}
protected function _copy(DomNode $context, DomNode $source, $xpath)
{
$this->findControl($context, $xpath)->appendChild($new = $source->cloneNode(true));
return $new;
}
public function copy($xpath)
{
if(!$this->canCopy($xpath))
throw new NotAllowedException($this->node->nodeName.': '.__FUNCTION__);
return new WritableXMLFSControl
( $this->_copy
( $this->file->getNode()
, $this->node
, $xpath
)
, $this->file
, $this->xpath
);
}
public function canCopyBefore($xpath)
{
$this->_copyBefore($this->test, $this->node->cloneNode(true), $xpath);
return $this->testIsValid();
}
protected function _copyBefore(DomNode $context, DomNode $source, $xpath)
{
$target = $this->findControl($context, $xpath);
$target->parentNode->insertBefore($new = $source->cloneNode(true), $target);
return $new;
}
public function copyBefore($xpath)
{
if(!$this->canCopyBefore($xpath))
throw new NotAllowedException($this->node->nodeName.': '.__FUNCTION__);
return new WritableXMLFSControl
( $this->_cloneBefore
( $this->file->getNode()
, $this->node
, $xpath
)
, $this->file
, $this->xpath
);
}
public function canCopyAfter($xpath)
{
$this->_copyAfter($this->test, $this->node->cloneNode(true), $xpath);
return $this->testIsValid();
}
protected function _copyAfter(DomNode $context, DomNode $source, $xpath)
{
$new = $source->cloneNode(true);
$target = $this->findControl($context, $xpath);
if($target->nextSibling)
$target->parentNode->insertBefore
( $new
, $target->nextSibling
);
else
$target->parentNode->appendChild($new);
return $new;
}
public function copyAfter($xpath)
{
if(!$this->canCopyAfter($xpath))
throw new NotAllowedException($this->node->nodeName.': '.__FUNCTION__);
return new WritableXMLFSControl
( $this->_copyAfter
( $this->file->getNode()
, $this->node
, $xpath
)
, $this->file
, $this->xpath
);
}
public function canAppendControl($controlName)
{
$this->_appendControl($context = $this->node->cloneNode(true), $controlName);
return $this->testIsValid($context);
}
protected function _appendControl(DomNode $target, $controlName)
{
$target->appendChild($new = $this->dom->createElementNS($this->namespace, $controlName));
return $new;
}
public function appendControl($controlName)
{
if(!self::controlExists($controlName))
throw new Exception('Control '.$controlName.' does not exist');
if(!$this->canAppendControl($controlName))
throw new NotAllowedException($this->node->nodeName.': '.__FUNCTION__);
return new WritableXMLFSControl
( $this->_appendControl
( $this->node
, $controlName
)
, $this->file
, $this->xpath
);
}
public function canInsertBefore($controlName)
{
$this->_insertBefore($context = $this->node->cloneNode(true), $controlName);
return $this->testIsValid($context);
}
/**
*
* @todo doesn't work with a cloned node, because it hasn't got a parent node.
*/
public function _insertBefore(DomNode $target, $controlName)
{
$target->parentNode->insertBefore
( $new = $this->dom->createElementNS
( $this->namespace
, $controlName
)
, $target
);
return $new;
}
public function insertBefore($controlName)
{
if(!self::controlExists($controlName))
throw new Exception('Control '.$controlName.' does not exist');
if(!$this->canInsertBefore($controlName))
throw new NotAllowedException($controlName);
return new WritableXMLFSControl
( $this->_insertBefore
( $this->node
, $controlName
)
, $this->file
, $this->xpath
);
}
public function canInsertAfter($controlName)
{
return self::controlExists($controlName);
}
public function insertAfter($controlName)
{
if(!$this->canInsertAfter($controlName))
throw new NotAllowedException($controlName);
$new = $this->dom->createElementNS($this->namespace, $controlName);
if($this->node->nextSibling)
$this->node->parentNode->insertBefore
( $new
, $this->node->nextSibling
);
else
$this->node->parentNode->appendChild($new);
return new WritableXMLFSControl($new, $this->file, $this->xpath);
}
public function canReplace($xpath)
{
return true;
}
public function replace($xpath)
{
if(!$this->canReplace($xpath))
throw new NotAllowedException($this->node->nodeName.': '.__FUNCTION__);
$target = $this->findControl($xpath);
$target->parentNode->replaceChild($this->node, $target);
return $this;
}
public function availableSubControls()
{
//array_unshift($this->relevantControls, '#text'); // Prepend the value to the array
return $this->relevantControls;
//return self::getAllControls();
}
public function getOptions()
{
return array_keys($this->control->getDefaultOptions());
}
public function getOption($name)
{
return $this->node->getAttribute($name);
}
public function getDefaultOption($name)
{
$options = $this->control->getDefaultOptions();
return $options[$name];
}
public function getOptionType($name)
{
return 'string';
}
public function canSetText($text)
{
return true;
}
public function setText($text)
{
if($this->canSetText($text))
$this->clear()->getNode()->appendChild($this->dom->createTextNode($text));
}
public function canSetOption($name, $value)
{
return true;
}
public function setOption($name, $value)
{
if(!$this->canSetOption($name, $value))
throw new NotAllowedException($name.': '.$value);
if(in_array($name, $this->getOptions()) && $this->getOptionType($name) === $this->getValueType($value))
{
if($this->getDefaultOption($name) == $value)
$this->removeAttribute($name);
else
$this->setAttribute($name, $value);
}
else
throw new NotAllowedException('Option: '.$name);
return $this;
}
public function canDelete()
{
return !in_array($this->getName(), $this->undeletableControls);
}
public function delete()
{
if(!$this->canDelete())
throw new NotAllowedException('delete');
$this->node->parentNode->removeChild($this->node);
unset($this);
}
public function clear()
{
while($this->node->childNodes->length > 0)
$this->node->removeChild($this->node->firstChild);
return $this;
}
public function getNode()
{
return $this->node;
}
public function getControl()
{
return $this->control;
}
public function subControls()
{
$subControls = array();
foreach($this->getList() as $child)
{
if($child->nodeType === XML_TEXT_NODE)
$subControls[] = new WritableXMLFSControlText($child, $this->file, $this->xpath);
else
$subControls[] = new WritableXMLFSControl($child, $this->file, $this->xpath);
}
return $subControls;
}
public function getList()
{
$list = array();
if($this->node->hasChildNodes())
foreach($this->node->childNodes as $child)
if(!in_array($child->nodeName, $this->excludeNodes))
$list[] = $child;
return $list;
}
public function getName()
{
return $this->node->nodeName;
}
public function createXPath()
{
$xpath = '';
$ns = 'mocovi';
foreach($this->getContext() as $current)
$xpath .= '/'.$ns.':'.$current->name.'['.$current->position.']';
return $xpath.'/'.$ns.':'.$this->getName().'['.self::getPosition($this->getNode()).']';
}
protected static function getPosition(DomNode $node)
{
$i = 1;
foreach($node->parentNode->childNodes as $child)
{
if($child === $node)
return $i;
if($child->nodeName == $node->nodeName)
$i++;
}
}
protected function getContext()
{
$parent = $this->getNode();
$context = array();
while(($parent = $parent->parentNode) && $parent->nodeName != 'model')
$context[] = (object)array('name' => $parent->nodeName, 'position' => self::getPosition($parent));
return array_reverse($context);
}
protected function newControl(DomNode $new)
{
return new WritableXMLFSControl($new, $this->file, $this->xpath);
}
protected function findControl(DomNode $context, $xpath)
{
if($control = $this->xpath->query('mocovi:model'.(substr($xpath, 0, 1) != '/' ?'/' : '').$xpath, $context)->item(0))
return $control;
throw new Exception('Target control not found at Xpath: '.$xpath);
}
protected function getValueType($value)
{
return 'string';
}
protected function removeAttribute($name)
{
$this->node->removeAttribute($name);
}
protected function setAttribute($name, $value)
{
$this->node->setAttribute($name, $value);
}
protected function testIsValid(DomNode $context = null)
{
$validate = new MocoviRelaxNGValidator(($context ? $context : $this->test));
return $validate->isValid();
}
}
class WritableXMLFSControlText extends WritableXMLFSControl
{
public function __construct(DomNode $node, WritableXMLFSFile $file, DomXpath $xpath)
{
$this->node = $node;
$this->file = $file;
$this->test = $file->getNode()->cloneNode(true);
$this->dom = $node->ownerDocument;
$this->xpath = $xpath;
$this->namespace = Mocovi::$namespace;
}
public function canAppendControl($controlName)
{
return false;
}
public function availableSubControls()
{
return array();
}
public function getOptions()
{
return array_keys($this->control->getDefaultOptions());
}
public function canSetText($text)
{
return true;
}
public function setText($text)
{
if($this->canSetText($text))
$this->clear()->getNode()->insertData(0, $text);
}
public function canSetOption($name, $value)
{
return false;
}
public function setOption($name, $value)
{
throw new NotAllowedException($this->node->nodeName.': '.__FUNCTION__);
}
public function clear()
{
$this->node->deleteData(0, strlen($this->node->wholeText));
return $this;
}
public function subControls()
{
return array();
}
public function getList()
{
throw new NotAllowedException($this->node->nodeName.': '.__FUNCTION__);
}
public function createXPath()
{
return str_replace('mocovi:#text', 'text()', parent::createXPath());
}
protected function removeAttribute($name)
{
throw new NotAllowedException($this->node->nodeName.': '.__FUNCTION__);
}
protected function setAttribute($name, $value)
{
throw new NotAllowedException($this->node->nodeName.': '.__FUNCTION__);
}
}
abstract class SchemaValidator
{
abstract public function __construct(DomElement $start);
abstract public function isValid();
}
//$x = new MocoviRelaxNGValidator(new DomElement('root'));
//$x->getValidSubControls('headline');
class MocoviRelaxNGValidator extends SchemaValidator
{
protected $relaxNGNamespace = 'http://relaxng.org/ns/structure/1.0';
protected $excludeFromAny = array
( 'root'
, 'xmlfs'
, 'file'
);
protected $extension = '.rng';
protected $template ='<?xml version="1.0" encoding="utf-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<start>
<!-- will be filled dynamically -->
</start>
<define name="any">
<zeroOrMore>
<choice>
<text/>
<!-- will be filled dynamically -->
</choice>
</zeroOrMore>
</define>
</grammar>';
protected $relaxNG;
protected $contextNode;
protected $xpath;
protected $start;
protected $any;
public function __construct(DomElement $start)
{
libxml_use_internal_errors(true);
$this->relaxNG = new DomDocument();
$this->relaxNG->loadXML($this->template);
$this->xpath = new DomXpath($this->relaxNG);
$this->xpath->registerNameSpace('rng', $this->relaxNGNamespace);
$this->setStartElement($start->nodeName);
$this->contextNode = $start;
$this->setDefinitions();
}
public function isValid()
{
/*
if(version_compare(LIBXML_DOTTED_VERSION, '2.7.6', '<')) // LibXML bug switch. Libxml < 2.7.6 produces an invalid XML even though it's correct!
return true;
$dom = new DomDocument('1.0', 'utf-8');
$dom->appendChild($dom->importNode($this->contextNode, true));
$return = $dom->relaxNGValidateSource($this->relaxNG->saveXML());
if($errors = $this->errors())
{
//echo $this->relaxNG->saveXML();
//echo $dom->saveXML();
//print_r($errors);
}
return $return;
*/
return true;
}
public function getValidSubControls($controlName)
{
//echo $this->relaxNG->saveXML();
$this->getRelaxNGElements
(
$this
->xpath
->query('//rng:element[@name="'.$controlName.'"]/*')
);
}
protected function getRelaxNGElements(DomNodeList $list)
{
static $elements = array();
foreach($list as $child)
switch($child->nodeName)
{
case 'interleave':
case 'choice':
$this->getRelaxNGElements($child->childNodes);
break;
case 'element':
$elements[] = $child->getAttribute('name');
}
}
protected function setStartElement($name)
{
$this->start = $this->xpath->query('/rng:grammar/rng:start')->item(0);
$this->start->appendChild($ref = $this->relaxNG->createElementNS($this->relaxNGNamespace, 'ref'));
$ref->setAttribute('name', $name);
$this->any = $this->xpath->query('/rng:grammar/rng:define[@name = "any"]/rng:zeroOrMore/rng:choice')->item(0);
}
protected function setDefinitions()
{
$anyElements = array();
if(!is_dir($GLOBALS['definitions']))
throw new Exception('Couldn\'t open definitions.');
foreach(glob(preg_quote($GLOBALS['definitions'], DIRECTORY_SEPARATOR).'[^_\.]*\\'.$this->extension) as $file)
{
if(!in_array($basename = basename($file, $this->extension), $this->excludeFromAny))
{
$this->any->appendChild($ref = $this->relaxNG->createElementNS($this->relaxNGNamespace, 'ref'));
$ref->setAttribute('name', $basename);
}
$tempDom = new DomDocument();
$tempDom->load($file);
$tempDom->preserveWhiteSpace = false;
$this->start->parentNode->appendChild($this->relaxNG->importNode($tempDom->documentElement, true));
}
}
protected function errors()
{
$errors = libxml_get_errors();
libxml_clear_errors();
return (count($errors) > 0 ? $errors : null);
}
}