Completed
Push — master ( 314506...335380 )
by mw
100:54 queued 62:54
created

includes/export/SMW_Serializer_RDFXML.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * File holding the SMWRDFXMLSerializer class that provides basic functions for
5
 * serialising OWL data in RDF/XML syntax.
6
 *
7
 * @ingroup SMW
8
 *
9
 * @author Markus Krötzsch
10
 */
11
12
/**
13
 * Class for serializing exported data (encoded as SMWExpData object) in
14
 * RDF/XML.
15
 *
16
 * @ingroup SMW
17
 */
18
class SMWRDFXMLSerializer extends SMWSerializer {
19
	/**
20
	 * True if the $pre_ns_buffer contains the beginning of a namespace
21
	 * declaration block to which further declarations for the current
22
	 * context can be appended.
23
	 */
24
	protected $namespace_block_started;
25
	/**
26
	 * True if the namespaces that are added at the current serialization stage
27
	 * become global, i.e. remain available for all later contexts. This is the
28
	 * case in RDF/XML only as long as the header has not been streamed to the
29
	 * client (reflected herein by calling flushContent()). Later, namespaces
30
	 * can only be added locally to individual elements, thus requiring them to
31
	 * be re-added multiple times if used in many elements.
32
	 */
33
	protected $namespaces_are_global;
34
35 14
	public function clear() {
36 14
		parent::clear();
37 14
		$this->namespaces_are_global = false;
38 14
		$this->namespace_block_started = false;
39 14
	}
40
41 14
	protected function serializeHeader() {
42 14
		$this->namespaces_are_global = true;
43 14
		$this->namespace_block_started = true;
44 14
		$this->pre_ns_buffer =
45
			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" .
46
			"<!DOCTYPE rdf:RDF[\n" .
47 14
			"\t<!ENTITY rdf " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&rdf;' ) ) . ">\n" .
48 14
			"\t<!ENTITY rdfs " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&rdfs;' ) ) . ">\n" .
49 14
			"\t<!ENTITY owl " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&owl;' ) ) . ">\n" .
50 14
			"\t<!ENTITY swivt " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&swivt;' ) ) . ">\n" .
51
			// A note on "wiki": this namespace is crucial as a fallback when it would be illegal to start e.g. with a number.
52
			// In this case, one can always use wiki:... followed by "_" and possibly some namespace, since _ is legal as a first character.
53 14
			"\t<!ENTITY wiki "  . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&wiki;' ) ) . ">\n" .
54 14
			"\t<!ENTITY category " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&category;' ) ) . ">\n" .
55 14
			"\t<!ENTITY property " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&property;' ) ) . ">\n" .
56 14
			"\t<!ENTITY wikiurl " . $this->makeValueEntityString( SMWExporter::getInstance()->expandURI( '&wikiurl;' ) ) . ">\n" .
57 14
			"]>\n\n" .
58 14
			"<rdf:RDF\n" .
59 14
			"\txmlns:rdf=\"&rdf;\"\n" .
60 14
			"\txmlns:rdfs=\"&rdfs;\"\n" .
61 14
			"\txmlns:owl =\"&owl;\"\n" .
62 14
			"\txmlns:swivt=\"&swivt;\"\n" .
63 14
			"\txmlns:wiki=\"&wiki;\"\n" .
64 14
			"\txmlns:category=\"&category;\"\n" .
65 14
			"\txmlns:property=\"&property;\"";
66 14
		$this->global_namespaces = array( 'rdf' => true, 'rdfs' => true, 'owl' => true, 'swivt' => true, 'wiki' => true, 'property' => true, 'category' => true );
67 14
		$this->post_ns_buffer .= ">\n\n";
68 14
	}
69
70 14
	protected function serializeFooter() {
71 14
		$this->post_ns_buffer .= "\t<!-- Created by Semantic MediaWiki, https://www.semantic-mediawiki.org/ -->\n";
72 14
		$this->post_ns_buffer .= '</rdf:RDF>';
73 14
	}
74
75 14
	public function serializeDeclaration( $uri, $typename ) {
76 14
		$this->post_ns_buffer .= "\t<$typename rdf:about=\"$uri\" />\n";
77 14
	}
78
79 14
	public function serializeExpData( SMWExpData $expData ) {
80 14
		$this->serializeNestedExpData( $expData, '' );
81 14
		$this->serializeNamespaces();
82 14
		if ( !$this->namespaces_are_global ) {
83 1
			$this->pre_ns_buffer .= $this->post_ns_buffer;
84 1
			$this->post_ns_buffer = '';
85 1
			$this->namespace_block_started = false;
86
		}
87 14
	}
88
89 14
	public function flushContent() {
90 14
		$result = parent::flushContent();
91 14
		$this->namespaces_are_global = false; // must not be done before calling the parent method (which may declare namespaces)
92 14
		$this->namespace_block_started = false;
93 14
		return $result;
94
	}
95
96 3
	protected function serializeNamespace( $shortname, $uri ) {
97 3
		if ( $this->namespaces_are_global ) {
98 3
			$this->global_namespaces[$shortname] = true;
99 3
			$this->pre_ns_buffer .= "\n\t";
100
		} else {
101
			$this->pre_ns_buffer .= ' ';
102
		}
103 3
		$this->pre_ns_buffer .= "xmlns:$shortname=\"$uri\"";
104 3
	}
105
106
	/**
107
	 * Serialize the given SMWExpData object, possibly recursively with
108
	 * increased indentation.
109
	 *
110
	 * @param $expData SMWExpData containing the data to be serialised.
111
	 * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
112
	 */
113 14
	protected function serializeNestedExpData( SMWExpData $expData, $indent ) {
114 14
		$this->recordDeclarationTypes( $expData );
115
116 14
		$type = $expData->extractMainType()->getQName();
117 14
		if ( !$this->namespace_block_started ) { // start new ns block
118 1
			$this->pre_ns_buffer .= "\t$indent<$type";
119 1
			$this->namespace_block_started = true;
120
		} else { // continue running block
121 14
			$this->post_ns_buffer .= "\t$indent<$type";
122
		}
123
124 14
		if ( ( $expData->getSubject() instanceof SMWExpResource ) &&
125 14
		      !$expData->getSubject()->isBlankNode() ) {
126 14
			 $this->post_ns_buffer .= ' rdf:about="' . $expData->getSubject()->getUri() . '"';
127
		} // else: blank node, no "rdf:about"
128
129 14
		if ( count( $expData->getProperties() ) == 0 ) { // nothing else to export
130
			$this->post_ns_buffer .= " />\n";
131
		} else { // process data
132 14
			$this->post_ns_buffer .= ">\n";
133
134 14
			foreach ( $expData->getProperties() as $property ) {
135 14
				$prop_decl_queued = false;
136 14
				$isClassTypeProp = $this->isOWLClassTypeProperty( $property );
137
138 14
				foreach ( $expData->getValues( $property ) as $valueElement ) {
139 14
					$this->requireNamespace( $property->getNamespaceID(), $property->getNamespace() );
140
141 14
					if ( $valueElement instanceof SMWExpLiteral ) {
142 14
						$prop_decl_type = SMW_SERIALIZER_DECL_APROP;
143 14
						$this->serializeExpLiteral( $property, $valueElement, "\t\t$indent" );
144
					} elseif ( $valueElement instanceof SMWExpResource ) {
145 14
						$prop_decl_type = SMW_SERIALIZER_DECL_OPROP;
146 14
						$this->serializeExpResource( $property, $valueElement, "\t\t$indent", $isClassTypeProp );
147
					} elseif ( $valueElement instanceof SMWExpData ) {
148 1
						$prop_decl_type = SMW_SERIALIZER_DECL_OPROP;
149
150 1
						$collection = $valueElement->getCollection();
151 1
						if ( $collection !== false ) { // RDF-style collection (list)
152
							$this->serializeExpCollection( $property, $collection, "\t\t$indent", $isClassTypeProp );
153 1
						} elseif ( count( $valueElement->getProperties() ) > 0 ) { // resource with data
154
							$this->post_ns_buffer .= "\t\t$indent<" . $property->getQName() . ">\n";
155
							$this->serializeNestedExpData( $valueElement, "\t\t$indent" );
156
							$this->post_ns_buffer .= "\t\t$indent</" . $property->getQName() . ">\n";
157
						} else { // resource without data
158 1
							$this->serializeExpResource( $property, $valueElement->getSubject(), "\t\t$indent", $isClassTypeProp );
159
						}
160
					} // else: no other types of export elements
161
162 14
					if ( !$prop_decl_queued ) {
163 14
						$this->requireDeclaration( $property, $prop_decl_type );
164 14
						$prop_decl_queued = true;
165
					}
166
				}
167
			}
168 14
			$this->post_ns_buffer .= "\t$indent</" . $type . ">\n";
169
		}
170 14
	}
171
172
	/**
173
	 * Add to the output a serialization of a property assignment where an
174
	 * SMWExpLiteral is the object. It is assumed that a suitable subject
175
	 * block has already been openend.
176
	 *
177
	 * @param $expResourceProperty SMWExpNsResource the property to use
178
	 * @param $expLiteral SMWExpLiteral the data value to use
179
	 * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
180
	 */
181 14
	protected function serializeExpLiteral( SMWExpNsResource $expResourceProperty, SMWExpLiteral $expLiteral, $indent ) {
182 14
		$this->post_ns_buffer .= $indent . '<' . $expResourceProperty->getQName();
183
184
		// https://www.w3.org/TR/rdf-syntax-grammar/#section-Syntax-languages
185
		// "... to indicate that the included content is in the given language.
186
		// Typed literals which includes XML literals are not affected by this
187
		// attribute. The most specific in-scope language present (if any) is
188
		// applied to property element string literal ..."
189 14
		if ( $expLiteral->getDatatype() !== '' && $expLiteral->getLang() !== '' ) {
190 1
			$this->post_ns_buffer .= ' xml:lang="' . $expLiteral->getLang() . '"';
191 14
		} elseif ( $expLiteral->getDatatype() !== '' ) {
192 14
			$this->post_ns_buffer .= ' rdf:datatype="' . $expLiteral->getDatatype() . '"';
193
		}
194
195 14
		$this->post_ns_buffer .= '>' . $this->makeAttributeValueString( $expLiteral->getLexicalForm() );
196 14
		$this->post_ns_buffer .= '</' . $expResourceProperty->getQName() . ">\n";
197 14
	}
198
199
	/**
200
	 * Add to the output a serialization of a property assignment where an
201
	 * SMWExpResource is the object. It is assumed that a suitable subject
202
	 * block has already been openend.
203
	 *
204
	 * @param $expResourceProperty SMWExpNsResource the property to use
205
	 * @param $expResource SMWExpResource the data value to use
206
	 * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
207
	 * @param $isClassTypeProp boolean whether the resource must be declared as a class
208
	 */
209 14
	protected function serializeExpResource( SMWExpNsResource $expResourceProperty, SMWExpResource $expResource, $indent, $isClassTypeProp ) {
210 14
		$this->post_ns_buffer .= $indent . '<' . $expResourceProperty->getQName();
211 14
		if ( !$expResource->isBlankNode() ) {
212 14
			if ( ( $expResource instanceof SMWExpNsResource ) && ( $expResource->getNamespaceID() == 'wiki' ) ) {
213
				// very common case, reduce bandwidth
214 9
				$this->post_ns_buffer .= ' rdf:resource="&wiki;' . $expResource->getLocalName() . '"';
215
			} else {
216 14
				$uriValue = $this->makeAttributeValueString( $expResource->getUri() );
217 14
				$this->post_ns_buffer .= ' rdf:resource="' . $uriValue . '"';
218
			}
219
		}
220 14
		$this->post_ns_buffer .= "/>\n";
221 14
		if ( $isClassTypeProp ) {
222 5
			$this->requireDeclaration( $expResource, SMW_SERIALIZER_DECL_CLASS );
223
		}
224 14
	}
225
226
	/**
227
	 * Add a serialization of the given SMWExpResource to the output,
228
	 * assuming that an opening property tag is alerady there.
229
	 *
230
	 * @param $expResourceProperty SMWExpNsResource the property to use
231
	 * @param $expResource array of (SMWExpResource or SMWExpData)
232
	 * @param $indent string specifying a prefix for indentation (usually a sequence of tabs)
233
	 * @param $isClassTypeProp boolean whether the resource must be declared as a class
234
	 *
235
	 * @bug The $isClassTypeProp parameter is not properly taken into account.
236
	 * @bug Individual resources are not serialised properly.
237
	 */
238
	protected function serializeExpCollection( SMWExpNsResource $expResourceProperty, array $collection, $indent, $isClassTypeProp ) {
239
		$this->post_ns_buffer .= $indent . '<' . $expResourceProperty->getQName() . " rdf:parseType=\"Collection\">\n";
240
		foreach ( $collection as $expElement ) {
241
			if ( $expElement instanceof SMWExpData ) {
242
				$this->serializeNestedExpData( $expElement, $indent );
243
			} else {
244
				// FIXME: the below is not the right thing to do here
245
				//$this->serializeExpResource( $expResourceProperty, $expElement, $indent );
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
246
			}
247
			if ( $isClassTypeProp ) {
248
				// FIXME: $expResource is undefined
249
				//$this->requireDeclaration( $expResource, SMW_SERIALIZER_DECL_CLASS );
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
250
			}
251
		}
252
		$this->post_ns_buffer .= "$indent</" . $expResourceProperty->getQName() . ">\n";
253
	}
254
255
	/**
256
	 * Escape a string in the special form that is required for values in
257
	 * DTD entity declarations in XML. Namely, this require the percent sign
258
	 * to be replaced.
259
	 *
260
	 * @param $string string to be escaped
261
	 * @return string
262
	 */
263 14
	protected function makeValueEntityString( $string ) {
264 14
		return "'" . str_replace( '%', '&#37;', $string ) . "'";
265
	}
266
267
	/**
268
	 * Escape a string as required for using it in XML attribute values.
269
	 *
270
	 * @param $string string to be escaped
271
	 * @return string
272
	 */
273 14
	protected function makeAttributeValueString( $string ) {
274 14
		return str_replace( array( '&', '>', '<' ), array( '&amp;', '&gt;', '&lt;' ), $string );
275
	}
276
277
}
278