diff --git a/src/SchemaReader.php b/src/SchemaReader.php
index 65de3f4f..5fb1d66e 100644
--- a/src/SchemaReader.php
+++ b/src/SchemaReader.php
@@ -54,6 +54,11 @@ class SchemaReader
*/
private $loadedFiles = array();
+ /**
+ * @var Schema[][]
+ */
+ private $loadedSchemas = array();
+
/**
* @var string[]
*/
@@ -1061,112 +1066,69 @@ private function loadImport(
Schema $schema,
DOMElement $node
): Closure {
- $base = urldecode($node->ownerDocument->documentURI);
- $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute('schemaLocation'));
-
$namespace = $node->getAttribute('namespace');
-
- $keys = $this->loadImportFreshKeys($namespace, $file);
-
- foreach ($keys as $key) {
- if (isset($this->loadedFiles[$key])) {
- $schema->addSchema($this->loadedFiles[$key]);
-
- return function () {
- };
- }
+ $schemaLocation = $node->getAttribute('schemaLocation');
+
+ // postpone schema loading
+ if ($namespace && !$schemaLocation && !isset(self::$globalSchemaInfo[$namespace])) {
+ return function () use ($schema, $namespace) {
+ if (!empty($this->loadedSchemas[$namespace])) {
+ foreach ($this->loadedSchemas[$namespace] as $s) {
+ $schema->addSchema($s, $namespace);
+ }
+ }
+ };
+ } elseif ($namespace && !$schemaLocation && isset($this->globalSchema[$namespace])) {
+ $schema->addSchema($this->globalSchema[$namespace]);
}
- return $this->loadImportFresh($namespace, $schema, $file);
- }
-
- /**
- * @return string[]
- */
- private function loadImportFreshKeys(
- string $namespace,
- string $file
- ): array {
- $globalSchemaInfo = $this->getGlobalSchemaInfo();
+ $base = urldecode($node->ownerDocument->documentURI);
+ $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute('schemaLocation'));
- $keys = [];
+ if (isset($this->loadedFiles[$file])) {
+ $schema->addSchema($this->loadedFiles[$file]);
- if (isset($globalSchemaInfo[$namespace])) {
- $keys[] = $globalSchemaInfo[$namespace];
+ return function () {
+ };
}
- $keys[] = $this->getNamespaceSpecificFileIndex(
- $file,
- $namespace
- );
-
- $keys[] = $file;
-
- return $keys;
+ return $this->loadImportFresh($namespace, $schema, $file);
}
- private function loadImportFreshCallbacksNewSchema(
- string $namespace,
+ private function createOrUseSchemaForNs(
Schema $schema,
- string $file
+ string $namespace
): Schema {
- /**
- * @var Schema $newSchema
- */
- $newSchema = $this->setLoadedFile(
- $file,
- (('' !== trim($namespace)) ? new Schema() : $schema)
- );
-
- if ('' !== trim($namespace)) {
+ if (('' !== trim($namespace))) {
+ $newSchema = new Schema();
$newSchema->addSchema($this->getGlobalSchema());
$schema->addSchema($newSchema);
+ } else {
+ $newSchema = $schema;
}
return $newSchema;
}
- /**
- * @return Closure[]
- */
- private function loadImportFreshCallbacks(
- string $namespace,
- Schema $schema,
- string $file
- ): array {
- /**
- * @var string
- */
- $file = $file;
-
- return $this->schemaNode(
- $this->loadImportFreshCallbacksNewSchema(
- $namespace,
- $schema,
- $file
- ),
- $this->getDOM(
- isset($this->knownLocationSchemas[$file])
- ? $this->knownLocationSchemas[$file]
- : $file
- )->documentElement,
- $schema
- );
- }
-
private function loadImportFresh(
string $namespace,
Schema $schema,
string $file
): Closure {
return function () use ($namespace, $schema, $file) {
- foreach (
- $this->loadImportFreshCallbacks(
- $namespace,
- $schema,
- $file
- ) as $callback
- ) {
+ $dom = $this->getDOM(
+ isset($this->knownLocationSchemas[$file])
+ ? $this->knownLocationSchemas[$file]
+ : $file
+ );
+
+ $schemaNew = $this->createOrUseSchemaForNs($schema, $namespace);
+
+ $this->setLoadedFile($file, $schemaNew);
+
+ $callbacks = $this->schemaNode($schemaNew, $dom->documentElement, $schema);
+
+ foreach ($callbacks as $callback) {
$callback();
}
};
@@ -1177,18 +1139,10 @@ private function loadImportFresh(
*/
protected $globalSchema;
- /**
- * @return string[]
- */
- public function getGlobalSchemaInfo(): array
- {
- return self::$globalSchemaInfo;
- }
-
/**
* @return Schema
*/
- public function getGlobalSchema(): Schema
+ private function getGlobalSchema(): Schema
{
if (!($this->globalSchema instanceof Schema)) {
$callbacks = array();
@@ -1240,30 +1194,55 @@ public function getGlobalSchema(): Schema
return $out;
}
- private function readNode(DOMElement $node, string $file = 'schema.xsd'): Schema
+ public function readNodes(array $nodes, string $file = null)
{
- $fileKey = $node->hasAttribute('targetNamespace') ? $this->getNamespaceSpecificFileIndex($file, $node->getAttribute('targetNamespace')) : $file;
- $this->setLoadedFile($fileKey, $rootSchema = new Schema());
-
+ $rootSchema = new Schema();
$rootSchema->addSchema($this->getGlobalSchema());
- $callbacks = $this->schemaNode($rootSchema, $node);
- foreach ($callbacks as $callback) {
+ if ($file !== null) {
+ $this->setLoadedFile($file, $rootSchema);
+ }
+
+ $all = array();
+ foreach ($nodes as $k => $node) {
+ if (($node instanceof \DOMElement) && $node->namespaceURI === self::XSD_NS && $node->localName == 'schema') {
+ $holderSchema = new Schema();
+ $holderSchema->addSchema($this->getGlobalSchema());
+
+ $this->setLoadedSchema($node, $holderSchema);
+
+ $rootSchema->addSchema($holderSchema);
+
+ $callbacks = $this->schemaNode($holderSchema, $node);
+ $all = array_merge($callbacks, $all);
+ }
+ }
+
+ foreach ($all as $callback) {
call_user_func($callback);
}
return $rootSchema;
}
- /**
- * It is possible that a single file contains multiple nodes, for instance in a WSDL file.
- *
- * Each of these nodes typically target a specific namespace. Append the target namespace to the
- * file to distinguish between multiple schemas in a single file.
- */
- private function getNamespaceSpecificFileIndex(string $file, string $targetNamespace): string
+ public function readNode(DOMElement $node, string $file = null): Schema
{
- return $file.'#'.$targetNamespace;
+ $rootSchema = new Schema();
+ $rootSchema->addSchema($this->getGlobalSchema());
+
+ if ($file !== null) {
+ $this->setLoadedFile($file, $rootSchema);
+ }
+
+ $this->setLoadedSchema($node, $rootSchema);
+
+ $callbacks = $this->schemaNode($rootSchema, $node);
+
+ foreach ($callbacks as $callback) {
+ call_user_func($callback);
+ }
+
+ return $rootSchema;
}
/**
@@ -1398,11 +1377,16 @@ private function findSomethingLikeAttributeGroup(
$addToThis->addAttribute($attribute);
}
- private function setLoadedFile(string $key, Schema $schema): Schema
+ private function setLoadedFile(string $key, Schema $schema): void
{
$this->loadedFiles[$key] = $schema;
+ }
- return $schema;
+ private function setLoadedSchema(DOMElement $node, Schema $schema): void
+ {
+ if ($node->hasAttribute('targetNamespace')) {
+ $this->loadedSchemas[$node->getAttribute('targetNamespace')][] = $schema;
+ }
}
private function setSchemaThingsFromNode(
diff --git a/tests/ImportTest.php b/tests/ImportTest.php
index b5303a76..7e698623 100644
--- a/tests/ImportTest.php
+++ b/tests/ImportTest.php
@@ -4,6 +4,9 @@
namespace GoetasWebservices\XML\XSDReader\Tests;
+use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
+use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
+
class ImportTest extends BaseTest
{
public function testBase()
@@ -37,4 +40,39 @@ public function testBase()
$this->assertSame($remoteAttr, $localAttrs[0]);
}
+
+ public function testBaseNode()
+ {
+ $dom = new \DOMDocument();
+ $dom->loadXML('
+
+
+
+ ');
+ $schema = $this->reader->readNode($dom->documentElement);
+ $attr = $schema->findAttribute('myAttribute', 'http://www.example.com');
+
+ $this->assertInstanceOf(AttributeItem::class, $attr);
+ }
+
+ public function testDependentImport()
+ {
+ $dom = new \DOMDocument();
+ $dom->loadXML('
+
+
+
+
+
+
+
+
+
+
+
+ ');
+ $schema = $this->reader->readNodes(iterator_to_array($dom->documentElement->childNodes));
+
+ $this->assertInstanceOf(ElementDef::class, $schema->findElement('outerEl', 'http://tempuri.org/1'));
+ }
}